home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 April: Mac OS SDK / Dev.CD Apr 99 SDK1.toast / Development Kits / Mac OS USB DDK / Examples / USBSampleStorageDriver / StorageClassUTFunctions.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-02-16  |  84.2 KB  |  2,765 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassUTFunctions.c
  3.  
  4.     Contains:    All device specific functions
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998-1999 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     File Ownership:
  11.  
  12.         DRI:                Craig Keithley
  13.  
  14.         Other Contact:        xxx put other contact here xxx
  15.  
  16.         Technology:            USB Drivers
  17.  
  18.     Writers:
  19.  
  20.         (TM)    Tim McLeod
  21.         (CJK)    Craig Keithley
  22.  
  23.     Change History (most recent first):
  24.  
  25.       <USB3>     2/16/99    TM        Removed internal parameter block initialization from
  26.                                     DriverStatusCmd routine and added a check for specific control
  27.                                     calls before initializing the parameter block in the
  28.                                     DriverControlCmd routine.
  29.       <USB2>     1/11/99    CJK        update to use sources from 1.1f3 DDK
  30.  
  31. */
  32.  
  33. #include <Disks.h>
  34. #include <DriverGestalt.h>
  35. #include <Gestalt.h>
  36. #include <NameRegistry.h>
  37. #include <Power.h>
  38. #include <Resources.h>
  39. #include <Strings.h>
  40.  
  41. #include "SampleStorageDriverAPI.h"
  42. #include "SampleStorageVersion.h"
  43. #include "StorageClassUTFunctions.h"
  44. #include "StorageClassUTDriver.h"
  45. #include "StorageClassUTDriverIcons.h"
  46.  
  47. enum 
  48. {
  49.     kReadWriteRetryCount = 1,
  50.     kFormatRequestSenseRetryCount = 5
  51. };
  52.     
  53. enum {
  54. //    kCDBSize = 12,
  55.     kSenseDataSize = 18
  56. };
  57.  
  58.  
  59. enum {
  60.     kCmdFormat                     = 0x04,
  61.     kCmdInquiry                 = 0x12,
  62.     kCmdModeSelect                 = 0x55,
  63.     kCmdModeSense                 = 0x5A,
  64.     kCmdPreventAllowRemoval     = 0x1E,
  65.     kCmdRead                     = 0x28,
  66.     kCmdReadCapacity             = 0x25,
  67.     kCmdReadFormatCapacities     = 0x23,
  68.     kCmdRequestSense             = 0x03,
  69.     kCmdRezero                     = 0x01,
  70.     kCmdSeek                     = 0x2B,
  71.     kCmdSendDiagnostic             = 0x1D,
  72.     kCmdStartStopUnit             = 0x1B,
  73.     kCmdTestUnitReady             = 0x00,
  74.     kCmdWrite                     = 0x2A,
  75.     kCmdWriteVerify             = 0x2E,
  76.     kCmdVerify                     = 0x2F
  77. };
  78.  
  79. // The structure for getting the media capacity from the device    
  80. struct ReadCapacityData 
  81. {
  82.     UInt32        lastLogicalBlock;        // Last valid LBA
  83.     UInt32        blockLength;            // Block length in bytes
  84. };
  85. typedef struct ReadCapacityData     ReadCapacityData, *ReadCapacityDataPtr;
  86.  
  87. // This structure defines each volume (partition) on a physical drive (media)
  88. struct VolumeRec                        // This is needed to support multiple partitions
  89. {
  90.     DrvSts2            driveStatus;        // drive status info & queue element
  91.     UInt16            vRefNum;            // drive number for this volume
  92.     Boolean            mountthispart;        // mount this volume indicator
  93.     Boolean            partmounted;        // volume mounted indicator
  94.     UInt32            partitionNo;        // the partition number for the volume
  95.     UInt32            partoffset;            // phys. offset of data partition
  96.     UInt32            curoffset;            // 0 = physical mapping, else 'partoffset'
  97.     UInt32            partblks;            // number of blocks in the partition
  98.     Ptr                nextVol;            // Link to next volume in the list
  99.     Ptr                drivePtr;            // Pointer to the owning drive's record
  100.     Ptr                mediaIconPtr;        // Pointer to the media icon for this volume
  101. };
  102. typedef    struct VolumeRec     VolumeRec, *VolumeRecPtr;
  103.  
  104. // This structure defines each physical drive…
  105. struct DriveRec
  106. {
  107.     UInt32            capacity;            // Drive capacity in blocks
  108.     UInt32            blockSize;
  109.     UInt16            numVolumes;            // Number of partitions in the drive
  110.     VolumeRecPtr    nextVol;            // Pointer to drive's volume list    
  111. };
  112. typedef struct DriveRec     DriveRec, *DriveRecPtr;
  113.  
  114. // Our Sleep Queue structure.  The driver installs a sleep queue proc to find
  115. // out when the machine goes into sleep or doze mode.  When a sleep or doze
  116. // notification is received, the driver will stop polling for media until a wake
  117. // notification is received.    
  118. struct OurSleepQRec
  119. {
  120.     SleepQRec    theSleepQRec;
  121.     Boolean        isInSleep;
  122. };
  123. typedef struct OurSleepQRec    OurSleepQRec, *OurSleepQRecPtr;
  124.  
  125. // The Drive Request PB structure contains all information need to perform
  126. // a device request
  127. struct DriveRequestPB
  128. {
  129.     StorageExecuteCommandPB        executePB;
  130.     ParmBlkPtr                    theIOPB;
  131.     IOCommandID                 ioCommandID;
  132.     IOCommandKind                 ioCommandKind;
  133.     volatile OSStatus            status;
  134.     UInt16                        retryCount;
  135.     Boolean                        doWrite;
  136. };
  137. typedef struct DriveRequestPB    DriveRequestPB, *DriveRequestPBPtr;
  138.  
  139. // The driver's global data structure
  140. struct UTDriverGlobals
  141. {
  142.     DriveRequestPB                drivePB;        
  143.  
  144.     // Global Control/Status fields for the driver
  145.     Boolean                        isFloppy;
  146.     Boolean                        isWriteProtected;
  147.     Boolean                        doInternalReadWrite;
  148.     Boolean                        diskInDrive;
  149.     UInt8                        currentExecutionState;
  150.     
  151.     SInt16                        drvrRefNum;            // Our driver reference number
  152.     ReadCapacityData            getCapacity;
  153.     UInt8                        sense[40];
  154.     DriveRec                    theDrive;
  155.     VolumeRec                    theVolume;
  156.     OurSleepQRec                theSleepQRec;
  157.     Str32                        DriveInfoString;
  158. };
  159. typedef struct UTDriverGlobals    UTDriverGlobals;
  160.  
  161. // The values for the states of the state machines
  162. enum
  163. {
  164.     // Mount State Machine
  165.     kMountStartState = 1,
  166.     kMountTURDoneState,
  167.     kMountRequestSenseDoneState,
  168.     kMountGetGeometryDoneState,
  169.     kMountCheckGetGeometryErrorDone,
  170.     kMountReadPossibleCapacitiesDone,
  171.     kMountCheckWriteProtectDoneState,
  172.     kMountPreventRemovalDoneState,
  173.  
  174.     // Eject State Machine
  175.     kEjectStartState,
  176.     kEjectAllowRemovalDone,
  177.     kEjectCartridgeDone,
  178.  
  179.     // Format State Machine
  180.     kFormatStartState,
  181.     kFormatDoneState,
  182.     kFormatWaitDoneState,
  183.     kFormatRequestSenseDoneState,
  184.     kFormatGetGeometryDoneState,
  185.     kFormatCheckWriteProtectDoneState,
  186.     kFormatPreventRemovalDoneState
  187. };
  188.  
  189. /* Driver Control/Status Codes for Startup Disk Control Panel */
  190. enum
  191. {
  192.     kcsSetBootPartitionCode            = 44,        // Set startup partition Control Call
  193.     kcsGetBootPartitionStatus        = 44        // Get startup partition Status Call
  194. };
  195.  
  196. // Prototypes for the Sleep Queue related functions
  197. static void             InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  198. static void             RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  199. static long             SleepNotification(long message, SleepQRecPtr sleepRec);
  200.  
  201. // Prototypes for the cartridge mounting related functions
  202. static OSStatus         MountSecondaryInterrupt( void *p1, void *p2);
  203. static void             MountTheCartridge( void *theCurrentPB );
  204.  
  205. // Prototypes for the cartridge ejecting related functions
  206. static void             EjectTheCartridge( void *theCurrentPB );
  207.  
  208. // Prototypes for the floppy formatting related functions
  209. static OSStatus         FormatCompletionInterrupt( void *p1, void *p2);
  210. static void             FormatTheCartridge( void *theCurrentPB );
  211.  
  212. // ATAPI/SCSI-2 Device Commands
  213. static OSStatus         CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  214. static OSStatus         TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  215. static OSStatus         GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  216. static OSStatus         ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  217. static OSStatus            RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  218. static OSStatus         FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  219. static OSStatus         EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  220. static OSStatus         PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion);
  221.  
  222. // Prototypes for the Read/Write functions and completion routines
  223. static OSStatus         DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite);
  224. static OSStatus         ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync);
  225. static void             ReadWriteCompletion( void *theDriverPB );
  226. static void             WriteRequestSenseCompletion( void *theDriverPB );
  227. static void             RequestSenseOnErrorCompletion( void *theDriverPB );
  228.  
  229. // Prototypes for the Drive Queue and Volume Queue related functions
  230. static void             InstallVolumes(DriveRecPtr theDrive, Boolean mountVols);
  231. static void                RemoveVolume(DriveRecPtr drvRec, UInt16 volRef);
  232. static VolumeRecPtr        GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum);
  233. static VCB*                MountedVolOfDrive(DriveRecPtr drive);
  234. static SInt16            NextQDrive();
  235. static VolumeRecPtr        CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset);
  236. static void             RemoveDrive(DriveRecPtr theDrivePtr);
  237. static void                UpdateQ(SInt16 qDrive, SInt32 newSize);
  238. static SInt32            NextPartitionID(DriveRecPtr drive);
  239. static Boolean             InstallDrive(DriveRecPtr drive, Boolean mountVols);
  240. static OSErr             MountVolumes( DriveRecPtr drive );
  241.  
  242. // This is to workaround a bug in the PowerPC native version of the AddDrive
  243. // call in systems before 8.5, where one needs to be added to the desired
  244. // drive number before calling AddDrive.
  245. // This function will check the system and pass the appropriate value to AddDrive
  246. static void             NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl);
  247.  
  248. //----------------------------------------------------------------------------------
  249. //                                    Globals
  250. //----------------------------------------------------------------------------------
  251.  
  252. static DriverLocationIcon                gDriveIcon;                            // static structure for control calls
  253. static StorageClassDispatchTablePtr        gItsTheDispatchTable = nil;            // The class's dispatch table
  254. static TimerID                            gInterruptTimer = 0;
  255. static UTDriverGlobals                    gTheUTGlobals;
  256.  
  257.  
  258. //----------------------------------------------------------------------------------
  259. //                                    Driver calls
  260. //----------------------------------------------------------------------------------
  261.  
  262. //    Always run at task level, can allocate and move memory.
  263. OSStatus DriverInitializeCmd (    AddressSpaceID        addressSpaceID,
  264.                                 DriverInitInfoPtr    initialInfo)
  265. {
  266. #pragma unused (addressSpaceID, initialInfo)
  267.     
  268.     OSStatus    err = noErr;
  269.  
  270.     IfDebugging("\pInitialize Driver");    
  271.     gTheUTGlobals.drvrRefNum = initialInfo->refNum;
  272.     gTheUTGlobals.drivePB.theIOPB = nil;
  273.     gTheUTGlobals.currentExecutionState = kMountStartState;
  274.     gTheUTGlobals.doInternalReadWrite = false;
  275.     gTheUTGlobals.diskInDrive = false;
  276.     
  277.     gTheUTGlobals.isFloppy = false;
  278.  
  279.     // Set up our color icon family
  280.     BuildMediaIconFamily();
  281.  
  282.     // Clear out the Drive Record
  283.     BlockZero((Ptr) &gTheUTGlobals.theDrive, sizeof(DriveRec));
  284.     
  285.     // Clear out the Volume Record
  286.     BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  287.     
  288.     // Build the Drive Info string returned in Status/Control calls
  289.     {
  290.         Str32 tempStr;
  291.         
  292.         PStrCopy( gTheUTGlobals.DriveInfoString, "\pUSB (v");
  293.         CStrToPStr(tempStr, kStorageStringVersShort); // driver version
  294.         PStrCat(gTheUTGlobals.DriveInfoString, tempStr); // driver version
  295.         PStrCat(gTheUTGlobals.DriveInfoString, "\p)");
  296.     }
  297.  
  298.     BlockZero((Ptr) &gTheUTGlobals.theSleepQRec, sizeof(OurSleepQRec));
  299.     InstallInSleepQueue( &gTheUTGlobals.theSleepQRec );
  300.     return (err);
  301. }
  302.  
  303. //    Always run at task level, can allocate and move memory.
  304. OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo)
  305. {
  306. #pragma unused (finalInfo)
  307.     OSStatus    err = noErr;
  308.  
  309.     DestroyMediaIconFamily();
  310.     RemoveFromSleepQueue( &gTheUTGlobals.theSleepQRec );    
  311.     
  312.     return (err);
  313. }
  314.  
  315. //    Always run at task level, can allocate and move memory.
  316. OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo)
  317. {
  318. #pragma unused (supersededInfo)
  319.  
  320.     OSErr    err = noErr;
  321.  
  322.     return (err);
  323. }
  324.  
  325. //    Always run at task level, can allocate and move memory.
  326. OSStatus DriverReplaceCmd (    AddressSpaceID            addressSpaceID,
  327.                             DriverReplaceInfoPtr    replaceInfo)
  328. {
  329. #pragma unused (addressSpaceID, replaceInfo)
  330.     OSStatus    err = noErr;
  331.  
  332.     return (err);
  333. }
  334.  
  335. //    Always run at task level, can allocate and move memory.
  336. OSStatus DriverOpenCmd ( AddressSpaceID    addressSpaceID, ParmBlkPtr pb)
  337. {
  338. #pragma unused (addressSpaceID, pb)
  339.     OSStatus    err         = noErr;
  340.  
  341.     // Let the world know we can do DriverGestalt calls
  342.     DriverGestaltOn(gTheUTGlobals.drvrRefNum);
  343.     
  344.     return(err);
  345. }
  346.  
  347. //    Always run at task level, can allocate and move memory.
  348. OSStatus DriverCloseCmd (ParmBlkPtr pb)
  349. {
  350. #pragma unused (pb)
  351.     OSStatus        err = noErr;
  352.  
  353.     gTheUTGlobals.diskInDrive = false;
  354.  
  355.     // If an interrupt timer is set, cancel it!
  356.     if( gInterruptTimer != 0 )
  357.     {
  358.         AbsoluteTime    timeLeft;
  359.         
  360.         // Cancel any pending timers
  361.         CancelTimer( gInterruptTimer, &timeLeft);
  362.         gInterruptTimer = 0;
  363.     }
  364.  
  365.     // If a command is pending, we should cancel it.
  366.     // Since we currently have no way of informing the Class driver to abort,
  367.     // we will wait for the class driver to tell us the command has finished.
  368.     while(    gTheUTGlobals.drivePB.theIOPB != nil );
  369.     
  370.     // Dequeue all drive volumes 
  371.     RemoveDrive(&gTheUTGlobals.theDrive);
  372.     
  373.     if(gItsTheDispatchTable != nil)
  374.     {
  375.         (void) ((gItsTheDispatchTable)->pStorageControl)(kControlEnableRemoval, nil);
  376.     }
  377.     
  378.     // Let the world know we can no longer doDriverGestalt calls
  379.     DriverGestaltOff(gTheUTGlobals.drvrRefNum);
  380.     
  381.     return (err);
  382. }
  383.  
  384. //    May run at interrupt level, CANNOT allocate or move memory.
  385. OSStatus DriverControlCmd (    AddressSpaceID    addressSpaceID,
  386.                             IOCommandID        ioCommandID,
  387.                             IOCommandKind    ioCommandKind,
  388.                             ParmBlkPtr        pb)
  389. {
  390. #pragma unused ( addressSpaceID )
  391.     OSStatus        err            = noErr;
  392.     CntrlParamPtr    pbPtr;
  393.     DriveRecPtr        drive         = &gTheUTGlobals.theDrive;
  394.  
  395.     pbPtr = (CntrlParamPtr) pb;
  396.     
  397.  
  398.     // Check if the control call is one that needs to use the parameter block
  399.     // if so, do the initialization on it.  There should be a check to make sure that
  400.     // the PB is not in use before it is initialized.
  401.     // This method will probably work because we shouldn't be getting these types of requests
  402.     // while reads and writes are happening, but this is not guaranteed
  403.     if ( (pbPtr->csCode == kVerify) || (pbPtr->csCode == kEject) || (pbPtr->csCode == kFormat)
  404.         || ( pbPtr->csCode == 21315 ))
  405.     {
  406.         // Should be a check here to make sure that the parameter block is not in use
  407.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  408.         
  409.         gTheUTGlobals.drivePB.theIOPB            = nil;
  410.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  411.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  412.         gTheUTGlobals.drivePB.retryCount         = 0;
  413.         gTheUTGlobals.drivePB.doWrite             = false;
  414.     }    
  415.  
  416.     // Parse the control codes…
  417.     switch(pbPtr->csCode) 
  418.     {
  419.         case killCode:
  420.         {
  421.             // This driver does not support the killCode,
  422.             // return back -1 as per TechNote DV 17: Sony Driver
  423.             err = -1;
  424.         }
  425.         break;
  426.         
  427.         case kVerify:                        // Verify the media, this should only be called for floppies
  428.         {
  429.             if(gTheUTGlobals.theDrive.capacity == 0)
  430.             {
  431.                 err = nsDrvErr;            // no Media is inserted, return an error
  432.             }
  433.             else if( gTheUTGlobals.isFloppy == true )
  434.             {
  435.                 UInt16     diskCapacity;
  436.                 UInt16     startBlock;
  437.                 UInt16    numberBlocks = 16;
  438.                 UInt8    *blockBuffer;
  439.                 
  440.                 diskCapacity = gTheUTGlobals.theDrive.capacity;
  441.                 blockBuffer = (UInt8 *) NewPtrSysClear(numberBlocks * gTheUTGlobals.theDrive.blockSize);
  442.                 if(blockBuffer == nil)
  443.                 {
  444.                     err = verErr;
  445.                 }
  446.                 else
  447.                 {
  448.                     for (startBlock = 0; startBlock<diskCapacity; startBlock+=numberBlocks)
  449.                     {
  450.                         gTheUTGlobals.doInternalReadWrite = true;
  451.                         err = ReadWriteBlock( &gTheUTGlobals, startBlock, numberBlocks,(Ptr)  blockBuffer, false, false);
  452.                         gTheUTGlobals.doInternalReadWrite = false;
  453.     
  454.                         // an Error has occured                    
  455.                         if ( err != noErr )
  456.                         {
  457.                             // report a verify error
  458.                             err = verErr;
  459.                             break;
  460.                         }
  461.                     }
  462.                     DisposePtr((Ptr) blockBuffer);
  463.                 }
  464.             }
  465.             else
  466.             {
  467.                 err = noErr;            // For the regular cartridge, just report noErr
  468.             }
  469.         }
  470.         break;
  471.  
  472.         case kFormat:
  473.         {
  474.             if (gTheUTGlobals.isWriteProtected == true)
  475.             {
  476.                 // check for write protect
  477.                 err = wPrErr;
  478.                 break;
  479.             }
  480.             
  481.             if(gTheUTGlobals.theDrive.capacity == 0)
  482.             {
  483.                 err = nsDrvErr;            // no Media is inserted, return an error
  484.             }
  485.             else if( gTheUTGlobals.isFloppy == true )
  486.             {
  487.                 gTheUTGlobals.currentExecutionState = kFormatStartState;
  488.                 FormatTheCartridge( &gTheUTGlobals );
  489.                 err = gTheUTGlobals.drivePB.status;
  490.             }
  491.             else
  492.             {
  493.                 err = noErr;            // For the regular cartridge, just report noErr
  494.             }
  495.         }
  496.         break;
  497.  
  498.         case kEject:
  499.         {
  500.             // We get this call whenever a volume is put away (dragged to trash).  
  501.             Boolean hasMountedVolume = (MountedVolOfDrive(drive) != nil);
  502.  
  503.             if (!hasMountedVolume)                    // If no more mounted volumes…
  504.             {
  505.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  506.                  EjectTheCartridge( &gTheUTGlobals );
  507.             }
  508.             
  509.         }
  510.         break;
  511.  
  512.         case kSetTagBuffer:                    // This is a floppy specific control call
  513.         {            
  514.             err = controlErr;                // Return a controlErr, since we do not support this call
  515.         }
  516.         break;
  517.  
  518.         case kTrackCache:                    // This is a floppy specific control call
  519.         {            
  520.             err = controlErr;                // The driver does not keep an internal write cache,
  521.                                             // therefore report that we do not support this call
  522.         }
  523.         break;
  524.  
  525.         case kDriveIcon:                    // Return icon displayed during media initialization
  526.         case kMediaIcon:                    // Return icon displayed on desktop for media
  527.         {
  528.             VolumeRecPtr    vol         = nil;        // pointer to our volume record structure
  529.             
  530.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  531.             if ( !vol )                                // If we did not find a drive and volume
  532.             {
  533.                 err = nsDrvErr;                        // report drive not found error
  534.                 break;
  535.             }
  536.                 
  537.             BlockMove(vol->mediaIconPtr, &gDriveIcon.LocationIcon, sizeof(DiskIcon));
  538.  
  539.             // Copy over the DriveInfo string from the global param block        
  540.             PStrCopy(gDriveIcon.LocationString, gTheUTGlobals.DriveInfoString);
  541.  
  542.             // Finally, return the pointer to the icon in csParam
  543.             *(DriverLocationIcon**)pbPtr->csParam = &gDriveIcon;    
  544.  
  545.             err = noErr;                // clear any error from above
  546.         }
  547.         break;
  548.  
  549.         case kDriveInfo:                    // DRIVE INFO request (was from ATA maanger)
  550.         {
  551.             pbPtr->csParam[0] = 0;            // Upper Word is always 0;
  552.             pbPtr->csParam[1] = 0;            // Clear Lower Word
  553.             
  554.             if( gTheUTGlobals.isFloppy == true )
  555.             {
  556.                 // If we currently have a floppy loaded, return the following info
  557.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  558.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  559.                                     |     ( 0 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  560.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  561.                                                         // Bits 4,5,6,7 are reserved
  562.                                     |     4;                // Drive Type ( use 4 for SuperDrive for compatibility )
  563.             }
  564.             else if(gTheUTGlobals.theDrive.blockSize != 0)
  565.             {
  566.                 // If we currently have a cartridge loaded, return the following info
  567.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  568.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  569.                                     |     ( 1 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  570.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  571.                                                         // Bits 4,5,6,7 are reserved
  572.                                     |     1;                // Drive Type ( use 1 for Unknown drive (Not a floppy) )
  573.             }
  574.  
  575.         }
  576.         break;
  577.  
  578.         
  579.         case kDriverConfigureCode:
  580.         {
  581.             DriverConfigParam             *configPtr;            // local pointer to drive config structure
  582.                         
  583.             configPtr = (DriverConfigParam *) pbPtr;
  584.             switch(configPtr->driverConfigureSelector) 
  585.             {
  586.                 case kdgFlush:
  587.                 {
  588.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  589.                     err = controlErr;
  590.                 }
  591.                 break;
  592.                 
  593.                 default:
  594.                 {
  595.                     err = controlErr;
  596.                 }
  597.                 break;
  598.             }
  599.         }
  600.         break;
  601.  
  602.         case kcsSetBootPartitionCode:                // Set Startup Partition
  603.         {
  604.             // Since there is currently no way to boot of USB,
  605.             // this control call will report that it is not supported
  606.             err = controlErr;
  607.         }
  608.         break;
  609.  
  610.         case kRegisterPartition:            // Register New Partition
  611.         {
  612.             // PC Exchange will call this function when it wants to redefine a partition.  It will
  613.             // pass in the drive queue element pointer of the partition to redefine, the
  614.             // new starting physical block offset, and the new block length.
  615.  
  616.             DrvQElPtr        theDrvQEl;                                // drive queue element pointer
  617.             VolumeRecPtr    vol         = nil;                        // pointer to our volume record structure
  618.             UInt32            *altParams;                                // local pointer to alternate control parameters
  619.     
  620.             altParams = (UInt32 *)&pbPtr->csParam[0];                // alternate parameters
  621.             
  622.             err = nsDrvErr;                                            // assume an invalid volume
  623.             theDrvQEl = (DrvQElPtr) altParams[THE_DRIVE];
  624.             if ( theDrvQEl )                                        // if valid queue element pointer
  625.             {
  626.                 vol = GetVolume(drive, theDrvQEl->dQDrive, 0);
  627.                 if ( vol )                                            // and valid volume reference
  628.                 {
  629.                     vol->partoffset = altParams[THE_PHYS_START];    // new partition offset
  630.                     vol->curoffset = vol->partoffset;                // current offset changes also
  631.                     vol->partblks = altParams[THE_PHYS_SIZE];        // new partition size
  632.                     
  633.                     UpdateQ(theDrvQEl->dQDrive, vol->partblks);        // Update drive queue capacity
  634.                     vol->partmounted = true;                        // volume will be mounted by PCX
  635.                     err = noErr;                                    // clear error
  636.                 }
  637.             }
  638.         }
  639.         break;
  640.             
  641.         case kGetADrive:                    // Get A Drive (Create New Partition)
  642.         {            
  643.             // PC Exchange calls this function to add a new partition.  The new partition's DrvQElPtr is
  644.             // returned. NOTE: If the driver handles multiple drives note that PC Exchange does not pass
  645.             // in the physical drive number on which to create the new partition.  However, the
  646.             // DrvQElPtr stored at the pointer passed in is for another partition on the drive.
  647.             UInt32            *altParams;                        // local pointer to alternate control parameters
  648.     
  649.             altParams = (UInt32 *)&pbPtr->csParam[0];        // alternate parameters
  650.         
  651.             if (!altParams[THE_VAR_QUEL])        // verify a valid queue element handle
  652.                 err = paramErr;
  653.             else
  654.             {
  655.                 // create a new volume record and DrvQEl associated with the physical drive.
  656.                 // The new volume starts at offset 0 and has no capacity yet. By default, the
  657.                 // partition will not have a partition map entry on the media.
  658.                 VolumeRecPtr    vol         = nil;            // pointer to our volume record structure
  659.                     
  660.                 vol = CreateVolume(drive, NextPartitionID(drive), 0, 0 );
  661.                 if ( vol ) 
  662.                 {
  663.                     UInt16 volNumber;
  664.                     
  665.                     vol->vRefNum = NextQDrive();        // assign a logical drive number
  666.                     volNumber = vol->vRefNum;
  667.                     
  668.                     NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  669.  
  670.                     // Return the DrvQElPtr at the location passed in…
  671.                     *((DrvQElPtr*)altParams[THE_VAR_QUEL]) = (DrvQElPtr) &vol->driveStatus.qLink;
  672.                 }
  673.                 else
  674.                     err = controlErr;
  675.             }
  676.         }
  677.         break;
  678.             
  679.         case kMediaPowerCSCode:                        // Set Power Mode
  680.         {
  681.             // The driver does not support power control modes
  682.             // report back a controlErr.
  683.             err = controlErr;
  684.         }
  685.         break;
  686.  
  687.         case 500:
  688.         {
  689.             AbsoluteTime    theWait;
  690.         
  691.             IfDebugging("\pSet Dispatch Table");
  692.             gItsTheDispatchTable = ( StorageClassDispatchTablePtr ) *((UInt32 *)(&pbPtr->csParam[0]));                // The class's dispatch table
  693.  
  694.             (void) ((gItsTheDispatchTable)->pStorageControl)(kControlDisableRemoval, nil);
  695.             theWait = DurationToAbsolute(durationSecond);
  696.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  697.             err = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  698.         }
  699.         break;
  700.  
  701.         case 21315:        // Disk copy format and copy
  702.         {
  703.             UInt16        theFormat;
  704.             UInt8        *theDiskImage;
  705.             
  706.             //SysDebugStr("\pDiskCopy");
  707.  
  708.             if( gTheUTGlobals.isWriteProtected == true)
  709.             {
  710.                 err = wPrErr;            // disk is write protected
  711.                 break;
  712.             }
  713.             
  714.             theFormat = pbPtr->csParam[0];
  715.             theDiskImage = (UInt8 *) *((UInt32 *) &pbPtr->csParam[1]);        // Extract the pointer to the data
  716.             gTheUTGlobals.doInternalReadWrite = true;
  717.             err = ReadWriteBlock( &gTheUTGlobals, 0, gTheUTGlobals.theDrive.capacity,(Ptr)  theDiskImage, true, false );
  718.             gTheUTGlobals.doInternalReadWrite = false;
  719.  
  720.             // an Error has occured                    
  721.             if ( err != noErr )
  722.             {
  723.                 // report an error
  724.                 err = paramErr;
  725.             }
  726.         }
  727.         break;
  728.         
  729.         default:
  730.         {
  731.             err = controlErr;
  732.         }
  733.         break;
  734.     }
  735.         
  736.     //SysDebugStr("\pEnd Control Call");
  737.     return(err);
  738. }
  739.  
  740. //    May run at interrupt level, CANNOT allocate or move memory.
  741.  
  742. OSStatus DriverStatusCmd (    AddressSpaceID    addressSpaceID,
  743.                             IOCommandID        ioCommandID,
  744.                             IOCommandKind    ioCommandKind,
  745.                             ParmBlkPtr        pb)
  746. {
  747. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  748.  
  749.     OSStatus            err            = noErr;
  750.     CntrlParamPtr         pbPtr;
  751.     DriveRecPtr            drive         = &gTheUTGlobals.theDrive;
  752.  
  753. #if 0
  754.     // This can be removed because this routine never invokes device access
  755.     // and always complete immediately.  If this a status call is added that
  756.     // doesn't complete immediately, then this routine will have to guarantee
  757.     // that the parameter block that it uses is not already in use.
  758.     BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  759.     
  760.     gTheUTGlobals.drivePB.theIOPB            = nil;
  761.     gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  762.     gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  763.     gTheUTGlobals.drivePB.retryCount         = 0;
  764.     gTheUTGlobals.drivePB.doWrite             = false;
  765. #endif
  766.  
  767.     pbPtr = (CntrlParamPtr) pb;
  768.  
  769.     // Finish processing the status call…
  770.     switch(pbPtr->csCode) 
  771.     {
  772.         case kReturnFormatList:
  773.         {
  774.             if( gTheUTGlobals.isFloppy == false )
  775.             {
  776.                 err = statusErr;
  777.             }
  778.             else
  779.             {
  780.                 typedef struct FormatList
  781.                 {
  782.                     UInt32        NumberBlock;
  783.                     UInt8        TSSValid         : 1;
  784.                     UInt8        IsCurrentFormat : 1;
  785.                     UInt8        CanFormat        : 1;
  786.                     UInt8        Density            : 1;
  787.                     UInt8        NoSides            : 4;
  788.                     UInt8        SecPerTrack;
  789.                     UInt16        NumberTracks;
  790.                 } FormatList;
  791.                 
  792.                 UInt8            returnNumber;
  793.                 UInt8            totalFormats = 2;
  794.                 FormatList        theFormats[2];
  795.                 
  796.                 // Setup the Formats
  797.                 // Double Density Floppy Disk
  798.                 theFormats[0].NumberBlock        = 1440;
  799.                 theFormats[0].TSSValid            = 1;
  800.                 theFormats[0].Density            = 0;            // 0 means single density, 1 means Double Density
  801.                 theFormats[0].NoSides            = 2;
  802.                 theFormats[0].SecPerTrack        = 9;
  803.                 theFormats[0].NumberTracks        = 80;
  804.                 
  805.                 // High Density Floppy Disk
  806.                 theFormats[1].NumberBlock        = 2880;
  807.                 theFormats[1].TSSValid            = 1;
  808.                 theFormats[1].Density            = 1;
  809.                 theFormats[1].NoSides            = 2;
  810.                 theFormats[1].SecPerTrack        = 18;
  811.                 theFormats[1].NumberTracks        = 80;
  812.  
  813.                 if( drive->capacity < 0x600 )
  814.                 {
  815.                     theFormats[0].CanFormat            = 0;        // 0 means we can format this disk as 720K
  816.                     theFormats[1].CanFormat            = 1;        // 1 means we can not format this disk as 1.44M
  817.                     theFormats[0].IsCurrentFormat    = 1;         // 1 means 720K MFM Disk is installed
  818.                     theFormats[1].IsCurrentFormat     = 0;        // 0 means 1.44M MFM Disk is not installed
  819.                 }
  820.                 else
  821.                 {
  822.                     theFormats[0].CanFormat            = 1;        // 1 means we can not format this disk as 720K
  823.                     theFormats[1].CanFormat            = 0;        // 0 means we can format this disk as 1.44M
  824.                     theFormats[0].IsCurrentFormat    = 0;         // 0 means 720K MFM Disk is not installed
  825.                     theFormats[1].IsCurrentFormat     = 1;        // 1 means 1.44M MFM Disk is installed
  826.                 }
  827.  
  828.                 returnNumber = *((UInt16 *) &pbPtr->csParam[0]);
  829.                 if( totalFormats < returnNumber )
  830.                 {
  831.                     returnNumber = totalFormats;
  832.                 }
  833.                 
  834.                 BlockCopy( (Ptr) &theFormats[0], (Ptr) (*((UInt32 *) &pbPtr->csParam[1])), sizeof(FormatList) * returnNumber);
  835.                 *((UInt16 *) &pbPtr->csParam[0]) = returnNumber;
  836.             }
  837.         }
  838.         break;
  839.     
  840.         case kDriveStatus:
  841.         {
  842.             VolumeRecPtr        vol         = nil;
  843.             
  844.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  845.             BlockMove(&vol->driveStatus, (UInt8 *) &pbPtr->csParam[0], sizeof(DrvSts));
  846.         }
  847.         break;
  848.     
  849.         case kMFMStatus:
  850.         {
  851.             if( gTheUTGlobals.isFloppy == false )
  852.             {
  853.                 err = statusErr;
  854.             }
  855.             else
  856.             {
  857.                 pbPtr->csParam[0] = -3;                            // PC Industry standard MFM (no GCR support)
  858.                 pbPtr->csParam[1] = -1;                            // MFM Disk installed
  859.                 
  860.                 if( drive->capacity < 0x600 )
  861.                 {
  862.                     pbPtr->csParam[2] = 0;                        // 720K MFM Disk installed
  863.                 }
  864.                 else
  865.                 {
  866.                     pbPtr->csParam[2] = -1;                        // 1.44M MFM Disk installed
  867.                 }
  868.                 
  869.                 pbPtr->csParam[3] = -5;                            // Generic PC Floppy Disk Controller
  870.             }
  871.         }
  872.         break;
  873.     
  874.         case kDriverGestaltCode:
  875.         {
  876.             DriverGestaltParam             *gestaltPtr;            // local pointer to drive gestalt structure
  877.             VolumeRecPtr                vol = nil;
  878.  
  879.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  880.             if (vol)
  881.                 drive = (DriveRecPtr) vol->drivePtr;            // get its drive record
  882.  
  883.             gestaltPtr = (DriverGestaltParam *) pbPtr;
  884.             switch(gestaltPtr->driverGestaltSelector) 
  885.             {
  886.                 case kdgVersion:
  887.                 {
  888.                     // Return information on the driver version
  889.                     NumVersion* numVersion;
  890.                     
  891.                     numVersion = GetDriverGestaltVersionResponse(gestaltPtr);
  892.                     
  893.                     numVersion->majorRev =            kStorageHexMajorVers;
  894.                     numVersion->minorAndBugRev =    kStorageHexMinorVers;
  895.                     numVersion->stage =                kStorageReleaseStage;
  896.                     numVersion->nonRelRev =            kStorageCurrentRelease;
  897.                 }
  898.                 break;
  899.                 
  900.                 case kdgDeviceType:
  901.                 {
  902.                     // Return the type of device--either floppy disk or removable disk
  903.                     DriverGestaltDevTResponse* deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr);
  904.  
  905.                     if( gTheUTGlobals.isFloppy == true )
  906.                     {
  907.                         deviceTypeResponse->deviceType = kdgFloppyType;
  908.                     }
  909.                     else
  910.                     {
  911.                         deviceTypeResponse->deviceType = kdgRemovableType;
  912.                     }
  913.                 }
  914.                 break;
  915.                 
  916.                 case kdgInterface:
  917.                 {
  918.                     // Return the interface of the drive in ioVRefNum.
  919.                     DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr);        
  920.  
  921.                     interfaceResponse->interfaceType = 'USB ';
  922.                 }
  923.                 break;
  924.  
  925.                 case kdgSync:
  926.                 {
  927.                     // Return true if the driver supports only synchronous behavior
  928.                     DriverGestaltSyncResponse*    syncResponse = GetDriverGestaltSyncResponse(gestaltPtr);
  929.  
  930.                     syncResponse->behavesSynchronously = false;
  931.                 }
  932.                 break;
  933.                     
  934.                 case kdgBoot:
  935.                 {
  936.                     // Return the ID of the boot device for PRAM storage
  937.                     if (!drive || !vol)
  938.                         err = nsDrvErr;
  939.                     else
  940.                     {
  941.                         // Since booting from USB is not yet supported, return statusErr.
  942.                         err = statusErr;
  943.                     }
  944.                 }
  945.                 break;
  946.  
  947.                 case kdgWide:
  948.                 {
  949.                     // Return whether driver supports large volume addressing (> 4GByte)
  950.                     // Unless our cartridges are greater than 4 GB, we don't support
  951.                     // or need to support wide block addressing
  952.                     Boolean* response = GetDriverGestaltBooleanResponse(gestaltPtr);
  953.                     
  954.                     *response = false; 
  955.                 }
  956.                 break;
  957.  
  958.                 case kdgPurge:        // Return if we can be closed and purged from memory.
  959.                 {
  960.                     DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr);
  961.                                         
  962.                     // Currently the driver cannot be closed or purged once installed
  963.                     response->purgePermission = kmNoCloseNoPurge;
  964.  
  965.                     response->purgeDriverPointer = (Ptr) nil;
  966.                 }
  967.                 break;
  968.                     
  969.                 case kdgSupportsSwitching:
  970.                 {
  971.                     // Return whether driver supports low power control call (csCode = 70h)
  972.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  973.                 }
  974.                 break;
  975.                     
  976.                 case kdgSupportsPowerCtl:
  977.                 {
  978.                     // Return whether driver supports low power control call (csCode = 70h)
  979.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  980.                 }
  981.                 break;
  982.                     
  983.                 case kdgAPI:
  984.                 {
  985.                     // Return whether driver supports PC-Exchange Control and Status calls
  986.                     // related to partitioning.
  987.                     DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr);
  988.                     
  989.                     apiResponse->partitionCmds = true;
  990.                 }
  991.                 break;
  992.                 
  993.                 case kdgFlush:
  994.                 {
  995.                     // Return whether driver supports the Cache flush Control call,
  996.                     // and whether the finder should tell us to flush our cache
  997.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  998.                     DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr);
  999.  
  1000.                     response->canFlush = false;
  1001.                     response->needsFlush = false;
  1002.                 }
  1003.                 break;
  1004.  
  1005.                 case kdgEject:
  1006.                 {
  1007.                     // Return whether driver wants eject call for shutdown and restart
  1008.                     // Eject on restart or shutdown
  1009.                     DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr);
  1010.                     response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject.
  1011.                 }
  1012.                 break;
  1013.  
  1014.                 case kdgVMOptions:
  1015.                 {
  1016.                     // Return whether drive can be used for Virtual Memory
  1017.                     // Don't support use of media for VM
  1018.                     DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr);
  1019.                     response->vmOptions = kAllowVMNoneMask;
  1020.                 }
  1021.                 break;
  1022.  
  1023.                 case kdgMediaInfo:
  1024.                 {
  1025.                     // Return back specific information about our media
  1026.                     DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr);
  1027.                     //response->numberBlocks =    vol->partblks;
  1028.                     response->numberBlocks =    gTheUTGlobals.theDrive.capacity;
  1029.                     response->blockSize =        gTheUTGlobals.theDrive.blockSize;
  1030.                     if(gTheUTGlobals.theDrive.blockSize == 0)
  1031.                     {
  1032.                         response->mediaType =        kMediaTypeNoMedia;
  1033.                     }
  1034.                     else
  1035.                     {
  1036.                         response->mediaType =        kMediaTypeUnknown;
  1037.                     }
  1038.                 }
  1039.                 break;
  1040.  
  1041.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1042.                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  1043.                 case kdgPhysDriveIconSuite:
  1044.  
  1045.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1046.                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  1047.                 case kdgMediaIconSuite:
  1048.                 {
  1049.                     // If the media is currently a floppy, return a statusErr so the system will
  1050.                     // handle the 3D Color icon information
  1051.                     if( gTheUTGlobals.isFloppy == true )
  1052.                     {
  1053.                         if(FloppyMediaIconFamily == nil )
  1054.                         {
  1055.                             err = statusErr;
  1056.                         }
  1057.                         else
  1058.                         {
  1059.                             gestaltPtr->driverGestaltResponse = (UInt32) *FloppyMediaIconFamily;
  1060.                         }
  1061.                     }
  1062.                     else
  1063.                     {
  1064.                         if(CartridgeMediaIconFamily == nil )
  1065.                         {
  1066.                             err = statusErr;
  1067.                         }
  1068.                         else
  1069.                         {
  1070.                             gestaltPtr->driverGestaltResponse = (UInt32) CartridgeMediaIconFamily;
  1071.                         }
  1072.                     }
  1073.                 }
  1074.                 break;
  1075.  
  1076.                 case kdgMediaName:
  1077.                 {
  1078.                     /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  1079.                     gestaltPtr->driverGestaltResponse = (UInt32) &gTheUTGlobals.DriveInfoString;
  1080.                 }
  1081.                 break;
  1082.  
  1083.                 default: 
  1084.                     err = statusErr;        // unknown DriverGestalt selector
  1085.             }
  1086.         }
  1087.         break;
  1088.         
  1089.         case kcsGetBootPartitionStatus:     // Is this the boot partition?
  1090.         {
  1091.             // Since USB drives don't support booting, always report false
  1092.             err = statusErr;
  1093.         }
  1094.         break;
  1095.         
  1096.         case kGetPartInfo:
  1097.         {
  1098.             // PC Exchange will call this function to get info on the specified partition. 
  1099.             // Return the physical drive reference, the starting block offset, and 
  1100.             // the partition ID (any relative non-zero reference).
  1101.     
  1102.             UInt32            *altParams;                // alternate csParams as long words
  1103.             partInfoRecPtr     thePartInfo;
  1104.             VolumeRecPtr    vol = nil;
  1105.             
  1106.             altParams = (UInt32 *) &pbPtr->csParam[0];            // alternate parameters
  1107.             thePartInfo = (partInfoRecPtr)altParams[kPartInfoResponse];
  1108.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  1109.     
  1110.             // SCSIID is not defined for non-SCSI devices.  For now we use the LUN only
  1111.             *((UInt32 *)&thePartInfo->SCSIID) = 0;
  1112.     
  1113.             thePartInfo->physPartitionLoc = vol->partoffset;
  1114.             thePartInfo->partitionNumber = vol->partitionNo;
  1115.         }
  1116.         break;
  1117.     
  1118.         case kMediaPowerCSCode:
  1119.         {
  1120.             // Driver does not support power control modes,
  1121.             // so just return a statusErr
  1122.             err = statusErr;
  1123.         }
  1124.         break;        
  1125.         
  1126.         case 17494:                            // DiskCopy version supported
  1127.         {
  1128.             pbPtr->csParam[0] = 0x0410;        // We support the Diskcopy 4.1 API
  1129.         }
  1130.         break;
  1131.         
  1132.         default:                            // Unrecognized status call
  1133.         {
  1134.             err = statusErr;
  1135.         }
  1136.         break;
  1137.     }
  1138.     //SysDebugStr("\pEnd Status Call");
  1139.     
  1140.     return(err);
  1141. }
  1142.  
  1143. //    May run at interrupt level, CANNOT allocate or move memory.
  1144.  
  1145. OSStatus DriverReadCmd (    AddressSpaceID    addressSpaceID,
  1146.                             IOCommandID        ioCommandID,
  1147.                             IOCommandKind    ioCommandKind,
  1148.                             ParmBlkPtr        pb)
  1149. {
  1150. #pragma unused ( addressSpaceID )
  1151.     OSStatus        err = ioErr;
  1152.  
  1153.     if( gTheUTGlobals.diskInDrive == false)
  1154.     {
  1155.         err = ioErr;
  1156.     }
  1157.     else
  1158.     {
  1159.         //SysDebugStr("\pRead Call;g");
  1160.         // Clear out the Drive request PB
  1161.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1162.         
  1163.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1164.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1165.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1166.         gTheUTGlobals.drivePB.retryCount         = 0;
  1167.         gTheUTGlobals.drivePB.doWrite             = false;
  1168.     
  1169.         err = DoReadWriteCommand( &gTheUTGlobals, false);
  1170.     }
  1171.     return(err);
  1172. }
  1173.  
  1174. //    May run at interrupt level, CANNOT allocate or move memory.
  1175.  
  1176. OSStatus DriverWriteCmd (    AddressSpaceID    addressSpaceID,
  1177.                             IOCommandID        ioCommandID,
  1178.                             IOCommandKind    ioCommandKind,
  1179.                             ParmBlkPtr        pb)
  1180. {
  1181. #pragma unused ( addressSpaceID )
  1182.     OSStatus        err = ioErr;
  1183.  
  1184.     if( gTheUTGlobals.diskInDrive == false )
  1185.     {
  1186.         err = ioErr;
  1187.     }
  1188.     else
  1189.     {
  1190.         //SysDebugStr("\pWrite Call");
  1191.         // Clear out the Drive request PB
  1192.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1193.     
  1194.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1195.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1196.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1197.         gTheUTGlobals.drivePB.retryCount         = 0;
  1198.         gTheUTGlobals.drivePB.doWrite             = true;
  1199.             
  1200.         err = DoReadWriteCommand( &gTheUTGlobals, true);
  1201.     }
  1202.     
  1203.     return(err);
  1204. }
  1205.  
  1206. //    May run at interrupt level, CANNOT allocate or move memory.
  1207.  
  1208. OSStatus DriverKillIOCmd (ParmBlkPtr pb)
  1209. {
  1210. #pragma unused (pb)
  1211.     OSStatus    err = noErr;
  1212.  
  1213.     return (err);
  1214. }
  1215.  
  1216. void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1217. {
  1218.     Boolean hasPMDispatch;
  1219.     SInt32    status;
  1220.  
  1221.     // To do most power management we must have the Power Manager Dispatch routines.      
  1222.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1223.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1224.  
  1225.     if (hasPMDispatch)                                                            
  1226.     {
  1227.         // Power Manager manages a spindown timer for the internal drive.  When the
  1228.         // timer expires it calls all routines registered in the HD Queue, and then,
  1229.         // if PG&E is present (Powerbooks) it will turn off power to the drive.
  1230.         // If PG&E is not present (Desktops) Power Manager can't turn off power and
  1231.         // expects one of the queue routines to reduce drive power instead. 
  1232.         //  Therefore, if we manage the internal drive we should be in the HD Queue.
  1233.  
  1234.             ourSleepQRec->theSleepQRec.sleepQLink = nil;                                
  1235.             ourSleepQRec->theSleepQRec.sleepQType = sleepQType;                            
  1236.             ourSleepQRec->theSleepQRec.sleepQProc = NewSleepQProc( &SleepNotification);        
  1237.             //ourSleepQRec->theSleepQRec.sleepQProc = (SleepQUPP) NewSleepQProc( &SleepNotification);        
  1238.             ourSleepQRec->theSleepQRec.sleepQFlags = 0;                // reserved    
  1239.             ourSleepQRec->isInSleep = false;    
  1240.             SleepQInstall( (SleepQRecPtr) ourSleepQRec );                            
  1241.     }                                                                            
  1242. }                                                                                
  1243.  
  1244. void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1245. {
  1246.     Boolean hasPMDispatch;
  1247.     SInt32    status;
  1248.  
  1249.     // To do most power management we must have the Power Manager Dispatch routines.      
  1250.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1251.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1252.  
  1253.     if (hasPMDispatch)                                                            
  1254.     {
  1255.         SleepQRemove( (SleepQRecPtr) ourSleepQRec );
  1256.     }
  1257. }                                                                                
  1258.  
  1259.  
  1260. long SleepNotification(long message, SleepQRecPtr sleepRec)
  1261. {
  1262.     OurSleepQRecPtr    ourSleepRec = (OurSleepQRecPtr) sleepRec;
  1263.     SInt32             response = noErr;                // assume we accept command
  1264.  
  1265.     switch(message)
  1266.     {
  1267.         case dozeRequest:                    // ##### Request to doze ######
  1268.         case dozeDemand:                    // ##### Going to doze now ######
  1269.         case sleepRequest:                    // ##### Request to sleep ######
  1270.         case sleepDemand:                    // ##### Going to sleep now ######
  1271.         case sleepNow:
  1272.             ourSleepRec->isInSleep = true;
  1273.             break;
  1274.  
  1275.         case sleepWakeUp:                    // ##### Wakeup from sleep ######
  1276.         case sleepRevoke:                    // ##### Someone denied sleep ######
  1277.         case dozeWakeUp:                    // ##### Wakeup from doze ######
  1278.             ourSleepRec->isInSleep = false;
  1279.             break;
  1280.     }
  1281.  
  1282.     return(response);
  1283. }
  1284.  
  1285.  
  1286. OSStatus MountSecondaryInterrupt( void *p1, void *p2)
  1287. {
  1288. #pragma unused ( p1, p2 )
  1289.     OSStatus             status;
  1290.     AbsoluteTime        theWait;
  1291.     UInt32                classDriverStatus;
  1292.  
  1293.     IfDebugging("\pMountSecondaryInterrupt");    
  1294.     gInterruptTimer = 0;
  1295.     
  1296.     if(gItsTheDispatchTable)
  1297.     {
  1298.         // Check if class driver is configured, if not setup another interrupt
  1299.         status = ((gItsTheDispatchTable)->pStorageStatus)(kStatusConfiguration, &classDriverStatus);
  1300.         if (status == noErr)
  1301.         {
  1302.             if ((classDriverStatus == kConfigureInProgress) || (gTheUTGlobals.theSleepQRec.isInSleep == true))
  1303.             {
  1304.                 theWait = DurationToAbsolute(durationSecond);
  1305.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1306.                 status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1307.                 
  1308.                 return noErr;
  1309.             } 
  1310.             else if (classDriverStatus == kConfigureComplete)
  1311.             {
  1312.                 gTheUTGlobals.currentExecutionState = kMountStartState;
  1313.                 MountTheCartridge( &gTheUTGlobals );
  1314.             }
  1315.         }
  1316.     }
  1317.     
  1318.     return noErr;
  1319. }
  1320.  
  1321.  
  1322. void MountTheCartridge( void *theCurrentPB )
  1323. {
  1324.     OSStatus         err = noErr;
  1325.     AbsoluteTime    oneSecondWait;
  1326.     DriveRec        *drive;
  1327.     UTDriverGlobals    *ourPB;
  1328.  
  1329.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1330.     drive = &ourPB->theDrive;
  1331.     oneSecondWait = DurationToAbsolute(durationSecond);
  1332.     //oneSecondWait = DurationToAbsolute(durationMillisecond * 20);
  1333.     switch (ourPB->currentExecutionState )
  1334.     {
  1335.         case kMountStartState:
  1336.         {
  1337.             ourPB->currentExecutionState = kMountTURDoneState;
  1338.             err = TUR( ourPB, &MountTheCartridge);
  1339.         }
  1340.         break;
  1341.         
  1342.         case kMountTURDoneState:
  1343.         {
  1344.             ourPB->currentExecutionState = kMountRequestSenseDoneState;
  1345.             err = RequestSense( ourPB, &MountTheCartridge);
  1346.         }
  1347.         break;
  1348.         
  1349.         case kMountRequestSenseDoneState:
  1350.         {
  1351.             if((ourPB->sense[12] == 0x00) && (ourPB->drivePB.executePB.status == noErr))
  1352.             {
  1353.                 ourPB->currentExecutionState = kMountGetGeometryDoneState;
  1354.                 ourPB->getCapacity.lastLogicalBlock = 0;
  1355.                 ourPB->getCapacity.blockLength = 0;
  1356.                 err = GetMediaGeometry( ourPB, &MountTheCartridge);
  1357.             }
  1358.             else
  1359.             {
  1360.                 ourPB->currentExecutionState = kMountStartState;
  1361.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1362.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1363.                 err = noErr;
  1364.             }
  1365.         }
  1366.         break;
  1367.  
  1368.         case kMountGetGeometryDoneState:
  1369.         {
  1370.             ReadCapacityData        *getGeometry;
  1371.             
  1372.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1373.  
  1374.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1375.             drive->blockSize = getGeometry->blockLength;
  1376.             
  1377.             if((drive->capacity == 0) || (drive->blockSize == 0))
  1378.             {
  1379.                 //Find out why the capacity is zero
  1380.                 ourPB->currentExecutionState = kMountCheckGetGeometryErrorDone;
  1381.                 err = RequestSense( ourPB, &MountTheCartridge);
  1382.                 break;
  1383.             }
  1384.  
  1385.             if(    drive->capacity >0x0C00)
  1386.             {
  1387.                 ourPB->isFloppy = false;
  1388.             }
  1389.             else
  1390.             {
  1391.                 ourPB->isFloppy = true;
  1392.             }
  1393.             
  1394.             ourPB->diskInDrive = true;
  1395.             
  1396.             // Check to see if media is write protected
  1397.             ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1398.             err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1399.         }
  1400.         break;
  1401.  
  1402.         case kMountCheckGetGeometryErrorDone:
  1403.         {
  1404.             if( (ourPB->sense[12] == 0x30) && (ourPB->sense[13] == 0x01))
  1405.             {
  1406.                 // We have an unformatted cartridge, find out what format it can have
  1407.                 ourPB->currentExecutionState = kMountReadPossibleCapacitiesDone;
  1408.                 err = ReadFormatCapacity(ourPB, &MountTheCartridge);
  1409.             }
  1410.             else
  1411.             {
  1412.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1413.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1414.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1415.             }
  1416.         }
  1417.         break;
  1418.         
  1419.         case kMountReadPossibleCapacitiesDone:
  1420.         {
  1421.             if( (ourPB->sense[3] >= 0x08) && (ourPB->sense[8] & 0x01 == 0x01))
  1422.             {
  1423.                 // this is the maximum format for this cartridge
  1424.                 drive->capacity = *((UInt32 *) &ourPB->sense[4]);
  1425.                 drive->blockSize = *((UInt16 *) &ourPB->sense[10]);
  1426.  
  1427.                 if(    drive->capacity >0x0C00)
  1428.                 {
  1429.                     ourPB->isFloppy = false;
  1430.                 }
  1431.                 else
  1432.                 {
  1433.                     ourPB->isFloppy = true;
  1434.                 }
  1435.                 
  1436.                 // Check to see if media is write protected
  1437.                 ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1438.                 err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1439.                 break;
  1440.             }
  1441.  
  1442.             // An error occurred.  It could be:
  1443.             //        1. we didn't get a complete descriptor
  1444.             //         2. the drive doesn't recognize this media type 
  1445.             // Reset and try again. ( Should we possibly eject the media instead??)
  1446.             ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1447.             oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1448.             err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1449.         }
  1450.         break;
  1451.         
  1452.         case kMountCheckWriteProtectDoneState:
  1453.         {
  1454.             if ( ( ourPB->sense[3] & 0x80 ) != 0 )
  1455.             {
  1456.                 ourPB->isWriteProtected = true;
  1457.             }
  1458.             else
  1459.             {
  1460.                 ourPB->isWriteProtected = false;
  1461.             }
  1462.             
  1463.             ourPB->currentExecutionState = kMountPreventRemovalDoneState;
  1464.             err = PreventAllowRemoval(ourPB, true, &MountTheCartridge);
  1465.         }
  1466.         break;
  1467.         
  1468.         case kMountPreventRemovalDoneState:
  1469.         {
  1470.             // The device is now mounted, and the Media is locked in place.
  1471.             // There is nothing left for us to do, so just break.
  1472.             if (InstallDrive( drive, true ) == false)
  1473.             {
  1474.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  1475.                  EjectTheCartridge( &gTheUTGlobals );
  1476.             }
  1477.         }
  1478.         break;
  1479.     }
  1480. }
  1481.  
  1482. void EjectTheCartridge( void *theCurrentPB )
  1483. {
  1484.     OSStatus         err = noErr;
  1485.     UTDriverGlobals    *ourPB;
  1486.  
  1487.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1488.     switch (ourPB->currentExecutionState )
  1489.     {
  1490.         case kEjectStartState:
  1491.         {
  1492.             ourPB->currentExecutionState = kEjectAllowRemovalDone;
  1493.             err = PreventAllowRemoval(ourPB, false, &EjectTheCartridge); // Allow for Ejects
  1494.         }
  1495.         break;
  1496.         
  1497.         case kEjectAllowRemovalDone:
  1498.         {
  1499.             ourPB->currentExecutionState = kEjectCartridgeDone;
  1500.             EjectCartridge(&gTheUTGlobals, EjectTheCartridge);
  1501.         }
  1502.         break;
  1503.         
  1504.         case kEjectCartridgeDone:
  1505.         {
  1506.             AbsoluteTime            theWait;
  1507.             StorageExecuteCommandPB    *commandPB;
  1508.             DriveRec                *drive;
  1509.         
  1510.             drive = &ourPB->theDrive;
  1511.             RemoveDrive(drive);
  1512.             commandPB = &ourPB->drivePB.executePB;                          // use a pointer to the executePB field
  1513.             ourPB->drivePB.status = commandPB->status;
  1514.             if(commandPB->status == noErr)
  1515.             {
  1516.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1517.                 theWait = DurationToAbsolute(durationSecond);
  1518.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1519.                 SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1520.             }
  1521.             
  1522.             drive->capacity = 0;
  1523.             drive->blockSize = 0;
  1524.             ourPB->isFloppy = false;
  1525.             ourPB->diskInDrive = false;
  1526.         }
  1527.         break;
  1528.     }
  1529. }
  1530.  
  1531. OSStatus FormatCompletionInterrupt( void *p1, void *p2)
  1532. {
  1533. #pragma unused ( p1, p2 )
  1534.     gInterruptTimer = 0;
  1535.     gTheUTGlobals.currentExecutionState = kFormatWaitDoneState;
  1536.     FormatTheCartridge( &gTheUTGlobals );
  1537.     return noErr;
  1538. }
  1539.  
  1540. void FormatTheCartridge( void *theCurrentPB )
  1541. {
  1542.     OSStatus         err = noErr;
  1543.     AbsoluteTime    oneSecondWait;
  1544.     DriveRec        *drive;
  1545.     UTDriverGlobals    *ourPB;
  1546.  
  1547.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1548.     drive = &ourPB->theDrive;
  1549.     oneSecondWait = DurationToAbsolute(durationSecond);
  1550.  
  1551.     switch (ourPB->currentExecutionState )
  1552.     {
  1553.         case kFormatStartState:
  1554.         {
  1555.             ourPB->currentExecutionState = kFormatDoneState;
  1556.             err = FormatFloppyCartridge( ourPB, &FormatTheCartridge);
  1557.         }
  1558.         break;
  1559.         
  1560.         case kFormatDoneState:
  1561.         {
  1562.             AbsoluteTime    theWait;
  1563.             
  1564.             theWait = DurationToAbsolute(durationSecond*35);
  1565.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1566.             SetInterruptTimer( &theWait, &FormatCompletionInterrupt, nil, &gInterruptTimer);
  1567.             err = 1;
  1568.         }
  1569.         break;
  1570.         
  1571.         case kFormatWaitDoneState:
  1572.         {
  1573.             ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1574.             ourPB->drivePB.retryCount = 0;
  1575.             err = RequestSense( ourPB, &FormatTheCartridge);
  1576.         }
  1577.         break;
  1578.         
  1579.         case kFormatRequestSenseDoneState:
  1580.         {
  1581.             //SysDebugStr("\pRequest is done");
  1582.             if(ourPB->drivePB.executePB.status == noErr)
  1583.             {
  1584.                 if(ourPB->sense[12] == 0x00)
  1585.                 {
  1586.                     ourPB->currentExecutionState = kFormatGetGeometryDoneState;
  1587.                     ourPB->getCapacity.lastLogicalBlock = 0;
  1588.                     ourPB->getCapacity.blockLength = 0;
  1589.                     err = GetMediaGeometry( ourPB, &FormatTheCartridge);
  1590.                 }
  1591.                 else
  1592.                 {
  1593.                     // The format has failed, inform the OS
  1594.                     err = controlErr;
  1595.                 }
  1596.             }
  1597.             else
  1598.             {
  1599.                 if (ourPB->drivePB.retryCount < kFormatRequestSenseRetryCount)
  1600.                 {
  1601.                     ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1602.                     ourPB->drivePB.retryCount += 1;
  1603.                     err = RequestSense( ourPB, &FormatTheCartridge);
  1604.                 }
  1605.                 else
  1606.                 {
  1607.                     // The format has failed, inform the OS
  1608.                     ourPB->drivePB.retryCount = 0;
  1609.                     err = controlErr;
  1610.                 }
  1611.             }
  1612.         }
  1613.         break;
  1614.  
  1615.         case kFormatGetGeometryDoneState:
  1616.         {
  1617.             ReadCapacityData        *getGeometry;
  1618.             
  1619.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1620.  
  1621.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1622.             drive->blockSize = getGeometry->blockLength;
  1623.             
  1624.             if( (ourPB->drivePB.executePB.status != noErr) || (drive->capacity == 0) || (drive->blockSize == 0))
  1625.             {
  1626.                 // The format has failed, inform the OS
  1627.                 err = controlErr;
  1628.                 break;
  1629.             }
  1630.  
  1631.             if(    drive->capacity >0x0C00)
  1632.             {
  1633.                 ourPB->isFloppy = false;
  1634.             }
  1635.             else
  1636.             {
  1637.                 ourPB->isFloppy = true;
  1638.             }
  1639.  
  1640.             ourPB->diskInDrive = true;
  1641.         }
  1642.         break;
  1643.     }
  1644.     
  1645.     ourPB->drivePB.status = err;
  1646.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  1647. }
  1648.  
  1649.  
  1650. OSStatus CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1651. {
  1652.     OSStatus    status = noErr;
  1653.     
  1654.     IfDebugging("\pCheck Write Protect");
  1655.  
  1656.     if (gItsTheDispatchTable)
  1657.     {
  1658.         StorageExecuteCommandPB    *commandPB;
  1659.         
  1660.         commandPB = &theCurrentPB->drivePB.executePB;                      // use a pointer to the executePB field
  1661.         
  1662.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));            // clear out the PB we will send
  1663.         BlockZero((Ptr) &theCurrentPB->sense[0], 8);
  1664.  
  1665.         commandPB->cdb[0] =            kCmdModeSense;
  1666.         commandPB->cdb[8] =            0x08;
  1667.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1668.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1669.         commandPB->expectedCount =    8;                                                // -> Expected number of bytes to transfer
  1670.         commandPB->completionProc =    ourCompletion;                                    // -> Completion routine
  1671.         
  1672.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1673.     }
  1674.     
  1675.     return status;
  1676. }
  1677.  
  1678. OSStatus TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1679. {
  1680.     OSStatus    status = noErr;
  1681.     
  1682.     IfDebugging("\pTUR");
  1683.  
  1684.     if (gItsTheDispatchTable)
  1685.     {
  1686.         StorageExecuteCommandPB    *commandPB;
  1687.         
  1688.         commandPB = &theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1689.         
  1690.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1691.     
  1692.         commandPB->flags =             kStorageNoData;    
  1693.         commandPB->completionProc =    ourCompletion;
  1694.         
  1695.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1696.     }
  1697.     
  1698.     return status;
  1699. }
  1700.  
  1701. OSStatus RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1702. {
  1703.     OSStatus                status = noErr;
  1704.  
  1705.     if (gItsTheDispatchTable)
  1706.     {
  1707.         StorageExecuteCommandPB    *commandPB;
  1708.         
  1709.         commandPB = (StorageExecuteCommandPB *)&theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1710.  
  1711.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1712.         BlockZero((Ptr) &theCurrentPB->sense[0], sizeof(kSenseDataSize));
  1713.         
  1714.         commandPB->cdb[0] =            kCmdRequestSense;
  1715.         commandPB->cdb[4] =            kSenseDataSize;
  1716.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1717.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];            // -> Pointer to user buffer
  1718.         commandPB->expectedCount =    kSenseDataSize;                            // -> Expected number of bytes to transfer
  1719.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1720.         
  1721.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1722.     }
  1723.     
  1724.     return status;
  1725. }
  1726.  
  1727. OSStatus GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1728. {
  1729.     OSStatus    status = noErr;
  1730.  
  1731.     IfDebugging("\pGetMediaGeometry");
  1732.  
  1733.     if (gItsTheDispatchTable)
  1734.     {
  1735.         ReadCapacityData        *getGeometry;
  1736.         StorageExecuteCommandPB    *commandPB;
  1737.         
  1738.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1739.         getGeometry = &theCurrentPB->getCapacity;
  1740.         
  1741.         getGeometry->lastLogicalBlock = 0;
  1742.         getGeometry->blockLength = 0;
  1743.         
  1744.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1745.  
  1746.         commandPB->cdb[0] =            kCmdReadCapacity;
  1747.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1748.         commandPB->userBuffer =        (Ptr) getGeometry;                        // -> Pointer to user buffer
  1749.         commandPB->expectedCount =    sizeof(ReadCapacityData);                // -> Expected number of bytes to transfer
  1750.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1751.         
  1752.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1753.     }
  1754.  
  1755.     return status;
  1756. }
  1757.  
  1758.  
  1759. OSStatus ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1760. {
  1761.     OSStatus    status = noErr;
  1762.  
  1763.     IfDebugging("\pReadFormatCapacity");
  1764.  
  1765.     if (gItsTheDispatchTable)
  1766.     {
  1767.         StorageExecuteCommandPB    *commandPB;
  1768.         
  1769.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1770.  
  1771.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1772.  
  1773.         commandPB->cdb[0] =            kCmdReadFormatCapacities;
  1774.         commandPB->cdb[7] =            0;
  1775.         commandPB->cdb[8] =            0x0C;
  1776.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1777.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1778.         commandPB->expectedCount =    0x0C;                                            // -> Expected number of bytes to transfer
  1779.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1780.         
  1781.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1782.     }
  1783.  
  1784.     return status;
  1785. }
  1786.  
  1787.  
  1788. OSStatus FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1789. {
  1790.     OSStatus    status = noErr;
  1791.  
  1792.     IfDebugging("\pFormatFloppyCartridge");
  1793.  
  1794.     if (gItsTheDispatchTable)
  1795.     {
  1796.         StorageExecuteCommandPB    *commandPB;
  1797.  
  1798.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1799.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1800.         BlockZero((Ptr) &theCurrentPB->sense[0], 0x0C);
  1801.  
  1802.         // Set up the data going out
  1803.         // First set up the Defect List Header
  1804.         theCurrentPB->sense[0] = 0;
  1805.         theCurrentPB->sense[1] = 0;
  1806.         theCurrentPB->sense[2] = 0;
  1807.         theCurrentPB->sense[3] = 0x08;
  1808.  
  1809.         *((UInt32 *) &theCurrentPB->sense[4]) = theCurrentPB->theDrive.capacity;
  1810.         *((UInt16 *) &theCurrentPB->sense[10]) = theCurrentPB->theDrive.blockSize;
  1811.         
  1812.         commandPB->cdb[0] =            kCmdFormat;
  1813.         commandPB->cdb[1] =            0x17;
  1814.         commandPB->flags =             kStorageDataOut;                    // -> Expect a data out transfer
  1815.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];        // -> Pointer to user buffer
  1816.         commandPB->expectedCount =    0x0C;                                // -> Expected number of bytes to transfer
  1817.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1818.  
  1819.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1820.     }
  1821.  
  1822.     return status;
  1823. }
  1824.  
  1825.  
  1826. OSStatus EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1827. {
  1828.     OSStatus    status = noErr;
  1829.  
  1830.     if (gItsTheDispatchTable)
  1831.     {
  1832.         StorageExecuteCommandPB    *commandPB;
  1833.         
  1834.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1835.  
  1836.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1837.         
  1838.         commandPB->cdb[0] =            kCmdStartStopUnit;
  1839.         commandPB->cdb[4] =            0x02;                            // Unload the Media
  1840.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1841.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1842.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1843.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1844.         
  1845.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1846.     }
  1847.  
  1848.     return status;
  1849. }
  1850.  
  1851.  
  1852. OSStatus PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion)
  1853. {
  1854.     OSStatus    status = noErr;
  1855.  
  1856.     if (gItsTheDispatchTable)
  1857.     {
  1858.         StorageExecuteCommandPB    *commandPB;
  1859.         
  1860.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1861.  
  1862.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1863.         
  1864.         commandPB->cdb[0] =            kCmdPreventAllowRemoval;
  1865.         if ( preventRemoval == true )
  1866.         {
  1867.             commandPB->cdb[4] =            0x01;                        // Prevent Media Removal
  1868.         }
  1869.         else
  1870.         {
  1871.             commandPB->cdb[4] =            0x00;                        // Allow Media Removal
  1872.         }
  1873.         
  1874.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1875.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1876.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1877.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1878.         
  1879.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1880.     }
  1881.  
  1882.     return status;
  1883. }
  1884.  
  1885.  
  1886. /* ---------------------Supporting Functions below this point -------------------------- */
  1887. //------------------------------------------------------------------------------
  1888. //    Function:        DRVRPrime
  1889. //    Description:    This is the ATA driver PRIME call that performs
  1890. //                    reading and writing to the device.
  1891. //                    
  1892. //    Input:            ioPB = Pointer to caller's I/O parameter block
  1893. //                    dce = Pointer to Device Control Entry (DCE)
  1894. //    Output:            A status code is returned.
  1895. //-------------------------------------------------------------------------------
  1896. OSStatus DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite )
  1897. {
  1898.     OSStatus        err;
  1899.     UInt32            startingBlock, numBlocks;
  1900.     VolumeRecPtr    vol;
  1901.     IOParamPtr         iopb;
  1902.     DriveRecPtr     drive;
  1903.     
  1904.     drive = &theDriverPB->theDrive;
  1905.     iopb = (IOParamPtr) theDriverPB->drivePB.theIOPB;
  1906.  
  1907.     // Find the associated volume and drive records required for the request.
  1908.     vol = GetVolume(drive, iopb->ioVRefNum, 0);            // Get the volume record requested 
  1909.     
  1910.     //if (vol)                                        // if we have a volume
  1911.     //    drive = (DriveRecPtr)vol->drivePtr;            // get its drive record
  1912.     //else                // assume request if for a physical drive
  1913.     if (!vol)                                        // if we have a volume
  1914.     {
  1915.         if (drive)                                    // if physical drive matches…
  1916.         {
  1917.             vol = drive->nextVol;
  1918.             if (vol)                                // and a volume exists for it…
  1919.             {
  1920.                 if (vol->curoffset)                    // and not doing physical addressing…
  1921.                 {
  1922.                     vol = 0;                        // the volume is invalid
  1923.                 }
  1924.             }
  1925.         }
  1926.     }
  1927.     
  1928.     if (!vol || !drive)                // Abort if we don't have both drive and volume records
  1929.         return(nsDrvErr);
  1930.         
  1931.     //................................................................................
  1932.     // A usable drive and volume exists.  Continue processing the request…
  1933.  
  1934.     // Compute the starting block address for the request.  We accept
  1935.     // only the normal 32 bit address and not the new 'Large Volume (64 bit) Addressing'.
  1936.     //SysDebugStr("\pCalculate the Read.");
  1937.     startingBlock = (UInt32)((iopb->ioPosOffset)/(drive->blockSize));
  1938.     numBlocks = (iopb->ioReqCount)/(drive->blockSize);
  1939.  
  1940.     if (doWrite && vol->driveStatus.writeProt)
  1941.     {
  1942.         // check for write protect
  1943.         err = wPrErr;
  1944.     }
  1945.     else if (iopb->ioReqCount & ((drive->blockSize) - 1))
  1946.     {
  1947.         // Verify if request is a multiple of the drives blocksize
  1948.         err = paramErr;
  1949.     }
  1950.     else if ( ( startingBlock + numBlocks ) > ((vol->curoffset) ? vol->partblks : drive->capacity))
  1951.     {
  1952.         // Verify if request is within range with respect to doing physical or logical I/O.
  1953.         // Access is limited to partition range (logical I/O) if curoffset is non-zero.
  1954.         err = paramErr;
  1955.     }
  1956.     else if ((iopb->ioPosMode & rdVerify) && !doWrite)
  1957.     {
  1958.         // If Read-Verify mode, this is not efficient with disks so we simply pretend we did it.
  1959.         iopb->ioActCount = iopb->ioReqCount;
  1960.         iopb->ioPosOffset += iopb->ioActCount;
  1961.         err = noErr;
  1962.     } 
  1963.     else                                    // Do the read or write
  1964.     {
  1965.         startingBlock += vol->curoffset;            // add in the partition offset
  1966.         err = ReadWriteBlock( theDriverPB, startingBlock, numBlocks, iopb->ioBuffer, doWrite, true );
  1967.     }
  1968.  
  1969.     return err;
  1970. }
  1971.  
  1972. //------------------------------------------------------------------------------
  1973. //    Function:        ReadWriteBlock
  1974. //
  1975. //    Description:    Low level read/write block on the media with retries.
  1976. //
  1977. //    Input:            drive:            Pointer to physical drive record
  1978. //                        blockAddr:        Starting block address 
  1979. //                        numBlocks:        Number of blocks to read/write
  1980. //                        buffer:            Pointer to buffer
  1981. //                        doWrite:            1 = write, 0 = read
  1982. //
  1983. //    Output:            true if successful, false if not
  1984. //-------------------------------------------------------------------------------
  1985. OSStatus ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync)
  1986. {    
  1987.     StorageExecuteCommandPBPtr     theReadWriteRequest;
  1988.     UInt32                        driveBlockSize;
  1989.     volatile OSStatus            status = noErr;
  1990.  
  1991.     IfDebugging("\p…ReadWriteBlock");
  1992.     theDriverPB->drivePB.status = noErr;
  1993.     driveBlockSize = theDriverPB->theDrive.blockSize;
  1994.     theReadWriteRequest = &(theDriverPB->drivePB.executePB);
  1995.  
  1996.     BlockZero(theReadWriteRequest, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1997.     theReadWriteRequest->userBuffer = buffer;                // -> Pointer to user buffer
  1998.     theReadWriteRequest->expectedCount = numBlocks*driveBlockSize;    // -> Expected number of bytes to transfer
  1999.  
  2000.     theReadWriteRequest->completionProc = (StorageClassCompletionProcPtr) ReadWriteCompletion;    // -> Completion routine
  2001.     theReadWriteRequest->actualCount = 0;                    // <- Actual number of bytes transferred
  2002.     theReadWriteRequest->status = 0;                    // <- Result of operation
  2003.  
  2004.     if(doWrite)
  2005.     {
  2006.         theReadWriteRequest->cdb[0] = kCmdWrite;
  2007.         theReadWriteRequest->flags     = kStorageDataOut;        // -> Expect a data out transfer
  2008.     }
  2009.     else
  2010.     {
  2011.         theReadWriteRequest->cdb[0] = kCmdRead;
  2012.         theReadWriteRequest->flags     = kStorageDataIn;        // -> Expect a data in transfer
  2013.     }
  2014.     
  2015.     // Set the starting block in the CDB
  2016.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  2017.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  2018.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  2019.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  2020.     
  2021.     // Set the Block Count in the CDB
  2022.     theReadWriteRequest->cdb[7] = (numBlocks >> 8) & 0xff;
  2023.     theReadWriteRequest->cdb[8] = numBlocks & 0xff;
  2024.  
  2025.     status = ((gItsTheDispatchTable)->pStorageExecuteCmd)( theReadWriteRequest );            
  2026.  
  2027.     theDriverPB->drivePB.status = status;
  2028.  
  2029.     if(status != 1)
  2030.     {
  2031.         theDriverPB->drivePB.theIOPB = nil;
  2032.     }
  2033.     else
  2034.     {
  2035.         if(doAsync == false)
  2036.         {
  2037.             while ( status == 1 )
  2038.             {
  2039.                 status = theDriverPB->drivePB.status;
  2040.             }
  2041.         }
  2042.     }
  2043.     
  2044.     return status;
  2045. }
  2046.  
  2047.  
  2048. void ReadWriteCompletion( void *theDriverPB )
  2049. {
  2050.     OSStatus            status;
  2051.     UTDriverGlobals        *ourPB;
  2052.     Boolean             wasWrite;
  2053.     
  2054.     //SysDebugStr("\pReadWriteCompletion;g");
  2055.     ourPB = (UTDriverGlobals *) theDriverPB;
  2056.     status = ourPB->drivePB.executePB.status;
  2057.     wasWrite = ((ourPB->drivePB.executePB.flags & kStorageDataOut) == kStorageDataOut);
  2058.     if(ourPB->doInternalReadWrite == false)
  2059.     {
  2060.         IOParamPtr         iopb;
  2061.  
  2062.         iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2063.         
  2064.         if( status == noErr )
  2065.         {
  2066.             if( wasWrite == true )
  2067.             {
  2068.                 // It was a write, do a Request sense to determine if any 
  2069.                 // errors occurred.
  2070.                 status = RequestSense( ourPB, &WriteRequestSenseCompletion);
  2071.                 
  2072.                 if( status != 1)
  2073.                 {
  2074.                     // An error occurred while trying to do the Request sense,
  2075.                     // return an ioErr to the system.
  2076.                     iopb->ioActCount = 0;
  2077.                     
  2078.                     // Set the status in the DriverPB last, this way if there is an immediate command,
  2079.                     // It won't think the command is done till after our processing.
  2080.                     ourPB->drivePB.status = ioErr;
  2081.                     
  2082.                     // Signal completion of the command to the operating system
  2083.                     ourPB->drivePB.theIOPB = nil;
  2084.                     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2085.                 }
  2086.             }
  2087.             else
  2088.             {
  2089.                 // This was a read command, and no errors occurred, finish the IO request and
  2090.                 // return to the system.
  2091.                 iopb->ioActCount = iopb->ioReqCount;
  2092.                 
  2093.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2094.                 // It won't think the command is done till after our processing.
  2095.                 ourPB->drivePB.status = noErr;
  2096.                 
  2097.                 // Signal completion of the command to the operating system
  2098.                 ourPB->drivePB.theIOPB = nil;
  2099.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2100.             }
  2101.         }
  2102.         else
  2103.         {
  2104.             // An error occurred on the command, do a Request sense to be certain any
  2105.             // USB Device stalls are cleared.
  2106.             status = RequestSense( ourPB, &RequestSenseOnErrorCompletion);
  2107.             
  2108.             if( status != 1)
  2109.             {
  2110.                 // An error occurred while trying to do the Request sense,
  2111.                 // return an ioErr to the system.
  2112.                 iopb->ioActCount = 0;
  2113.                 
  2114.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2115.                 // It won't think the command is done till after our processing.
  2116.                 ourPB->drivePB.status = ioErr;
  2117.                 
  2118.                 // Signal completion of the command to the operating system
  2119.                 ourPB->drivePB.theIOPB = nil;
  2120.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2121.             }
  2122.         }
  2123.     }
  2124.     else
  2125.     {
  2126.         // If this is an internal command, this is all we care about
  2127.         ourPB->drivePB.status = status;
  2128.     }
  2129.  
  2130.     IfDebugging("\p…ReadWriteBlock Done");
  2131. }
  2132.  
  2133. void WriteRequestSenseCompletion( void *theDriverPB )
  2134. {
  2135.     OSStatus        status;
  2136.     UTDriverGlobals    *ourPB;
  2137.     IOParamPtr         iopb;
  2138.     
  2139.     ourPB = (UTDriverGlobals *) theDriverPB;
  2140.     status = ourPB->drivePB.executePB.status;
  2141.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2142.  
  2143.     if( status == noErr )
  2144.     {
  2145.         if( ( ourPB->sense[2] & 0x0F !=0 ) && ( ourPB->sense[2] & 0x0F !=1 ))
  2146.         {
  2147.             // An error has been reported back in the sense key, return 
  2148.             // an ioErr to the system.
  2149.             iopb->ioActCount = 0;
  2150.             ourPB->drivePB.status = ioErr;
  2151.         }
  2152.         else
  2153.         {
  2154.             // No errors has been reported back in the sense key, return 
  2155.             // a noErr to the system.
  2156.             iopb->ioActCount = iopb->ioReqCount;
  2157.             ourPB->drivePB.status = noErr;
  2158.         }
  2159.     }
  2160.     else
  2161.     {
  2162.         // Errors occurred on the Request sense report an
  2163.         // ioErr back to the system
  2164.         iopb->ioActCount = 0;
  2165.         ourPB->drivePB.status = ioErr;
  2166.     }
  2167.  
  2168.     // Signal completion of the command to the operating system
  2169.     //SysDebugStr("\pWriteReqSenseFinishCommand");
  2170.     ourPB->drivePB.theIOPB = nil;
  2171.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2172. }
  2173.  
  2174. void RequestSenseOnErrorCompletion( void *theDriverPB )
  2175. {
  2176.     UTDriverGlobals    *ourPB;
  2177.     IOParamPtr         iopb;
  2178.     
  2179.     ourPB = (UTDriverGlobals *) theDriverPB;
  2180.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2181.         
  2182.     // An error occurred on the initial command, and no error occurred on the RequestSense
  2183.     if( (ourPB->drivePB.retryCount < kReadWriteRetryCount) && (ourPB->drivePB.executePB.status == noErr))
  2184.     {
  2185.         OSStatus err;
  2186.         // We have not yet exceeded the retry count, so try the operation again
  2187.             // Increment our retry counter
  2188.             ourPB->drivePB.retryCount += 1;
  2189.     
  2190.             // Send the command out again
  2191.             err = DoReadWriteCommand( ourPB, ourPB->drivePB.doWrite);
  2192.         
  2193.         if ( err !=1 )
  2194.         {
  2195.             iopb->ioActCount = 0;
  2196.             ourPB->drivePB.status = ioErr;
  2197.         
  2198.             // Signal completion of the command to the operating system
  2199.             ourPB->drivePB.theIOPB = nil;
  2200.             FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2201.         }
  2202.     }
  2203.     else
  2204.     {
  2205.         // Since we only get here if an error occurred on the orignal
  2206.         // command, and the Request Sense was to clear any device stalls.
  2207.         // We will always report back an ioErr to the system.
  2208.         iopb->ioActCount = 0;
  2209.         ourPB->drivePB.status = ioErr;
  2210.     
  2211.         // Signal completion of the command to the operating system
  2212.         ourPB->drivePB.theIOPB = nil;
  2213.         FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2214.     }
  2215. }
  2216.  
  2217.  
  2218. //------------------------------------------------------------------------------
  2219. //    Function:        GetVolume
  2220. //
  2221. //    Description:    Searches for the volume record with the specified vRefNum and 
  2222. //                    returns its VolumeRecPtr.  Optionally, partitionNum
  2223. //                    can be use as search keys if vRefNum is zero.
  2224. //
  2225. //    Input:            drive:            the pointer to the DriveRec
  2226. //                    vRefNum:        the volume's reference number
  2227. //                    partitionNum:    the volume's partition number
  2228. //
  2229. //    Output:            pointer to volume record if found, nil if not found
  2230. //-------------------------------------------------------------------------------
  2231. VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum)
  2232. {
  2233.     VolumeRecPtr vol = nil;
  2234.     Boolean found;
  2235.     
  2236.     // Search all volume records for one matching the search key
  2237.  
  2238.     vol = drive->nextVol;                        // first volume pointer
  2239.  
  2240.     while (vol)
  2241.     {
  2242.         found = (vRefNum) ? (vol->vRefNum == vRefNum) : (vol->partitionNo == partitionNum);
  2243.  
  2244.         if (found)
  2245.             return(vol);                        // found the partition
  2246.         else
  2247.             vol = (VolumeRecPtr)vol->nextVol;    // otherwise, get next volume pointer
  2248.     }
  2249.     
  2250.     return(vol);                                // nil if volume not found
  2251. }
  2252.  
  2253.  
  2254. //------------------------------------------------------------------------------
  2255. //    Function:        CreateVolume
  2256. //
  2257. //    Description:    This function creates a volume record for the specified drive.
  2258. //                        The volume is appended to the drive's volume queue and a logical
  2259. //                        logical drive is installed in the system drive queue.  It is 
  2260. //                        assumed the volume is not write protected, drive is installed,
  2261. //                        and the media is installed.  
  2262. //                    
  2263. //    Input:            drive:            pointer to the drive record of the volume to create
  2264. //                    partitionID:    the volume's partition ID
  2265. //                    volSize:        size of the volume in blocks
  2266. //                    volOffset:        block offset of volume on drive
  2267. //
  2268. //    Output:            Returns nil pointer if fails, else pointer to volume record
  2269. //
  2270. //    NOTE:            Assumes all inputs are valid!
  2271. //-------------------------------------------------------------------------------
  2272. VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset )
  2273. {
  2274.     register VolumeRecPtr        vol;
  2275.     VolumeRecPtr*                volHandle;
  2276.     Boolean                        volInQueue = false;
  2277.     
  2278.     // When a volume is offlined by the system (greyed desktop icon) it is still
  2279.     // being used and its associated DrvQEl must remain.  Thus, its volume and 
  2280.     // physical drive record also must remain.  So, when new media is inserted 
  2281.     // we need to check for these structures and reuse them before creating new ones.
  2282.     // The structures can be reused since the file system verifies the volume.
  2283.  
  2284.     if( drive->nextVol == nil)
  2285.     {
  2286.         // Make sure the drive's volume queue points to the volume
  2287.         vol = &gTheUTGlobals.theVolume;
  2288.     }
  2289.     else
  2290.     {
  2291.         // Search for a volume record for the volume (partition) to be created…
  2292.         vol = GetVolume(drive, 0, partitionID);
  2293.         
  2294.         if (vol)                                                        // if record exists for this volume…
  2295.         {
  2296.             volInQueue = true;                                            // remember volume is already in queue
  2297.     
  2298.             if (vol->driveStatus.diskInPlace)                            // if volume is already online…
  2299.                 vol = nil;                                                // we shouldn't be here - fall thru
  2300.         }
  2301.         else                                                            // need to create volume record
  2302.         {
  2303.             vol = (VolumeRecPtr) PoolAllocateResident(sizeof(VolumeRec), true);    // Allocate storage for record
  2304.         }
  2305.     }
  2306.     
  2307.     if (vol)                                                        // if a volume record is valid…
  2308.     {    
  2309.         if( gTheUTGlobals.isFloppy == true)
  2310.         {
  2311.             vol->driveStatus.track             = 80;            // Sectors on a MFM disk
  2312.             vol->driveStatus.sides             = -1;            // -1 means double sided floppy
  2313.         }
  2314.         else
  2315.         {
  2316.             vol->driveStatus.track             = 0;            // Only used on floppys
  2317.             vol->driveStatus.sides             = 0;            // Only used on floppys
  2318.         }        
  2319.  
  2320.         if( gTheUTGlobals.isWriteProtected == true)
  2321.         {
  2322.             vol->driveStatus.writeProt     = 0x80;            // disk is write protected
  2323.         }
  2324.         else
  2325.         {
  2326.             vol->driveStatus.writeProt     = 0;            // not write protected yet
  2327.         }
  2328.         vol->driveStatus.diskInPlace     = 1;            // Ejectable Disk
  2329.         vol->driveStatus.installed         = 1;            // drive is installed
  2330.         vol->driveStatus.dQFSID         = 0;                        // File Manager's volume type
  2331.         if( gTheUTGlobals.isFloppy == true)
  2332.         {
  2333.             DrvSts    *theDriveStatus;
  2334.             
  2335.             theDriveStatus = (DrvSts *) &vol->driveStatus;
  2336.             theDriveStatus->qType         = 0;                        // This is a floppy drive status structure
  2337.             theDriveStatus->twoSideFmt = -1;                        // after 1st rd/wrt: 0=1 side, -1=2 side
  2338.             theDriveStatus->needsFlush = -1;                        // -1 for MacPlus drive
  2339.             theDriveStatus->diskErrs = 0;                            // soft error count
  2340.  
  2341.             vol->mediaIconPtr =    (Ptr) AppleFloppyMediaIcon;        // media icon for floppies
  2342.         }
  2343.         else
  2344.         {
  2345.             vol->driveStatus.qType             = 1;                    // both dQDrvSz and dQDrvSz2 are used
  2346.             vol->driveStatus.driveSize        = (UInt16) volSize;        // volume size in blocks
  2347.             vol->driveStatus.driveS1         = (UInt16) (volSize >> 16);
  2348.             
  2349.             vol->mediaIconPtr =    (Ptr) &CartridgeIcon;                    // media icon for cartridge
  2350.         }
  2351.             
  2352.  
  2353.         vol->mountthispart =    false;                                // don't mount this volume,
  2354.         vol->partmounted =        false;                                // it's not mounted yet,
  2355.         vol->partitionNo =        partitionID;                        // save the partition ID
  2356.  
  2357.         // Save volume's block offset and set its access mode by setting curoffset to the
  2358.         // same (access is relative to partition offset if curoffset is non-zero, else physical)
  2359.         vol->curoffset =        volOffset;                            // block offset of volume
  2360.         vol->partoffset =        volOffset;                            // partition offset
  2361.         vol->partblks =            volSize;                            // save size for our use also
  2362.         vol->drivePtr =        (Ptr) drive;                            // pointer to vol's physical drive
  2363.         // If this record is not in our volume list yet, finish initializing and insert
  2364.         if (!volInQueue)                                            // If not in the volume queue…
  2365.         {
  2366.             vol->driveStatus.qLink = nil;                            // initialize system queue link
  2367.             vol->nextVol = nil;                                        // no link to next volume yet
  2368.  
  2369.             // Find the end of the drive's volume queue and link in the new volume record.
  2370.             volHandle = &(drive->nextVol);                            // point to start of drive's volume queue
  2371.             while (*volHandle)                                        // search for end of volume queue…
  2372.             {
  2373.                 volHandle = (VolumeRecPtr*)&((*volHandle)->nextVol);
  2374.             }
  2375.             
  2376.             *volHandle = vol;                                        // at end of queue, insert volume
  2377.         }
  2378.  
  2379.         drive->numVolumes++;                                        // update number of drive volumes
  2380.     }
  2381.     return(vol);
  2382. }
  2383.  
  2384.  
  2385. //------------------------------------------------------------------------------
  2386. //    Function:        InstallVolumes
  2387. //    Description:    Searches for partitions on the media and installs them as
  2388. //                        volumes for the associated drive.  Assumes the drive does
  2389. //                        not have any volumes installed yet. 
  2390. //
  2391. //    Input:            theDrive:    pointer to drive record
  2392. //                        mountVols:    true means to mount the drive's partitions
  2393. //
  2394. //-------------------------------------------------------------------------------
  2395. void InstallVolumes(DriveRecPtr theDrive, Boolean mountVols)
  2396. {
  2397.     VolumeRecPtr    vol = nil;
  2398.  
  2399.     // If the driver is going to support partitions, this is were the media should be 
  2400.     // scanned to see if there are any valid HFS or DOS partitions.
  2401.     
  2402.     // If no valid paritions are found, or partitions are not support, the following code
  2403.     // will create a volume of the entire media capacity, post a disk inserted
  2404.     // event and let the File System Manager try and figure it out.  Note we post a disk
  2405.     // inserted event rather than notifying FSM so if the media is not recognized (because
  2406.     // it's unformatted or the correct file system is not installed) the system will prompt
  2407.     // with a "This is not a Macintosh disk…" message.  If we call FSM instead, the user will
  2408.     // not be prompted when the media is unformatted.  This provides a way to format media.
  2409.  
  2410.     if (theDrive->numVolumes == 0)                            // if no partitions were found, or are not supported
  2411.     {
  2412.         IfDebugging("\ptheDrive->numVolumes == 0");
  2413.         vol = CreateVolume(theDrive, 1, theDrive->capacity, 0);
  2414.         if (vol)
  2415.         {
  2416.             vol->mountthispart = true;                                    // post disk inserted event later
  2417.         }
  2418.     }
  2419.  
  2420.     // Add the remaining volumes to the drive queue…
  2421.     vol = theDrive->nextVol;                                            // first volume of the drive
  2422.     while (vol)
  2423.     {
  2424.         if ((vol->mountthispart == true) || (mountVols == false))
  2425.         {
  2426.             UInt16 volNumber;
  2427.             
  2428.             vol->vRefNum = NextQDrive();        // assign a logical drive number
  2429.             volNumber = vol->vRefNum;
  2430.             
  2431.             NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  2432.         }
  2433.         
  2434.         vol = (VolumeRecPtr)vol->nextVol;                            // point to the next volume
  2435.     }
  2436. }
  2437.  
  2438.  
  2439. //------------------------------------------------------------------------------
  2440. //    Function:        RemoveVolume
  2441. //    Description:    Deletes a volume and its record
  2442. //    Input:            drvRec:        pointer to physical drive record of volume
  2443. //                    volRef:        Volume reference
  2444. //-------------------------------------------------------------------------------
  2445. void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef)
  2446. {
  2447.     VolumeRecPtr*    pvHandle = &(drvRec->nextVol);
  2448.     VolumeRecPtr    pvPtr = *pvHandle;
  2449.     
  2450.     while (pvPtr) 
  2451.     {
  2452.         if (pvPtr->vRefNum == volRef)             // found the volume record
  2453.         {        
  2454.             Dequeue((QElemPtr) &(pvPtr->driveStatus.qLink), GetDrvQHdr());    // remove from drive queue        
  2455.             *pvHandle = (VolumeRecPtr)(pvPtr->nextVol);    // remove it from the linked list
  2456.             if (pvPtr == &gTheUTGlobals.theVolume)
  2457.             {
  2458.                 BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  2459.             }
  2460.             else
  2461.             {
  2462.                 PoolDeallocate( pvPtr );            // release its memory
  2463.             }
  2464.             drvRec->numVolumes--;                // decrement number of drive volumes
  2465.             break;
  2466.         }
  2467.         
  2468.         pvHandle = (VolumeRecPtr*)&(pvPtr->nextVol);        // save pointer to pv pointer
  2469.         pvPtr = (VolumeRecPtr)pvPtr->nextVol;                // point to next pv
  2470.     }            
  2471. }
  2472.  
  2473.  
  2474. //------------------------------------------------------------------------------
  2475. //    Function:        InstallDrive
  2476. //
  2477. //    Description:    Installs a physical drive and its volumes under the driver's
  2478. //                        control.  The driver determines if the drive is one it can
  2479. //                        manage, and if so, creates and initializes the drive's record,
  2480. //                        sets the drives operating mode and options, and mounts its
  2481. //                        partitions to the system.
  2482. //
  2483. //    Input:            drvNum:        physical drive reference
  2484. //                        mountVols:    true means to mount drive's partitions
  2485. //
  2486. //    Output:            true if successful, false if not
  2487. //-------------------------------------------------------------------------------
  2488. Boolean InstallDrive(DriveRecPtr theDrive, Boolean mountVols)
  2489. {
  2490.     // Initialize variables associated with the new drive.
  2491.     theDrive->numVolumes =        0;                            // no partitions yet
  2492.     
  2493.     //..............................................................................
  2494.     // Search for file system partitions on the media and install them as volumes
  2495.     // of this drive.  If no volumes, the drive must be considered unusable.
  2496.     InstallVolumes(theDrive, mountVols);
  2497.  
  2498.     if (theDrive->numVolumes)
  2499.     {
  2500.         OSErr theErr;
  2501.         
  2502.         theErr = MountVolumes( theDrive );
  2503.         if(theErr != noErr)
  2504.         {
  2505.             // For some reason, the disk could not be mounted,
  2506.             // We should eject and let the user decide whether to try again.
  2507.             gTheUTGlobals.currentExecutionState = kEjectStartState;
  2508.             EjectTheCartridge( &gTheUTGlobals );
  2509.         }
  2510.     }
  2511.     else                                                    // Abort if no volumes installed for drive        
  2512.     {
  2513.         return(false);
  2514.     }
  2515.  
  2516.     return(true);
  2517. }
  2518.  
  2519.  
  2520. //------------------------------------------------------------------------------
  2521. //    Function:        RemoveDrive
  2522. //    Description:    Removes a physical drive and its volumes from our control
  2523. //                    
  2524. //-------------------------------------------------------------------------------
  2525. void RemoveDrive(DriveRecPtr theDrivePtr)
  2526. {        
  2527.     VolumeRecPtr    vol;
  2528.     UInt16            vrefnum;
  2529.     
  2530.     if (theDrivePtr)                                // If the drive exists…
  2531.     {
  2532.         while (theDrivePtr->nextVol)                // dequeue all volumes on the drive…
  2533.         {
  2534.             vol = theDrivePtr->nextVol;                // get volume
  2535.             vrefnum = vol->vRefNum;                    // get its refnum
  2536.             RemoveVolume(theDrivePtr, vrefnum);        // delete volume from our queue
  2537.         }
  2538.     }
  2539. }
  2540.  
  2541.  
  2542. //------------------------------------------------------------------------------
  2543. //    Function:        NextQDrive
  2544. //
  2545. //    Description:    Returns the next unused logical drive number from the system 
  2546. //                    Drive Queue.  The Drive Queue is searched starting with a
  2547. //                    logical drive number 8.
  2548. //                    
  2549. //    Input:            none
  2550. //
  2551. //    Output:            The highest Drive Queue drive number + 1
  2552. //-------------------------------------------------------------------------------
  2553. SInt16 NextQDrive( void )
  2554. {
  2555.     QHdrPtr    qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2556.     DrvQEl    *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2557.     SInt16    drv = 8;                            // Start above built in drives 
  2558.  
  2559.     while (qel)                                 // While not end of queue, 
  2560.     {
  2561.         if (qel->dQDrive == drv)                 // if drive number used, 
  2562.         {
  2563.             drv++;                                // bump number, and 
  2564.             qel = (DrvQEl*) (qhdr->qHead);        // search from start 
  2565.         }
  2566.         else                                    // else next queue element 
  2567.             qel = (DrvQEl*) (qel->qLink);
  2568.     }
  2569.  
  2570.     return(drv);                        // Return the next logical drive 
  2571. }
  2572.  
  2573.  
  2574. //------------------------------------------------------------------------------
  2575. //    Function:        FindMountedVol
  2576. //    Description:    Searches the volume queue for a mounted volume specified 
  2577. //                    by vRefNum.  If one is found, its VCB pointer is returned.
  2578. //                    
  2579. //    Input:            vRefNum:    the volume reference to search for
  2580. //
  2581. //    Output:            the VCB pointer (NULL = volume not mounted)
  2582. //-------------------------------------------------------------------------------
  2583. static VCB *FindMountedVol(SInt16 vRefNum)
  2584. {
  2585.     QHdrPtr    volQ = GetVCBQHdr();        // VCB queue head pointer
  2586.     VCB        *theVol = (volQ) ? (VCB*)volQ->qHead : 0;    // first VCB
  2587.  
  2588.     while(theVol)
  2589.     {
  2590.         // The test for whether a volume is mounted or not is done using
  2591.         // the VCB fields vcbDrvNum and vcbDRefNum.  A volume is mounted
  2592.         // only if it is online.
  2593.         //
  2594.         //                    online            offline            ejected
  2595.         //
  2596.         //    vcbDrvNum        >0 (DrvNum)         0                 0
  2597.         //    vcbDRefNum        <0 (DRefNum)    <0 (-DrvNum)    >0 (DrvNum)
  2598.     
  2599.         if (theVol->vcbDrvNum == vRefNum)    // if volume specified is online…
  2600.             break;                            // volume is mounted
  2601.  
  2602.         theVol = (VCB*)theVol->qLink;        // next VCB
  2603.     }
  2604.     
  2605.     return(theVol);
  2606. }
  2607.  
  2608. //------------------------------------------------------------------------------
  2609. //    Function:        MountedVolOfDrive
  2610. //
  2611. //    Description:    Searches the system VCB for a mounted volume on the specified 
  2612. //                    physical drive.
  2613. //                    
  2614. //    Input:            drive:    pointer to the physical drive's record
  2615. //
  2616. //    Output:            the VCB pointer (nil if no volume mounted for drive)
  2617. //-------------------------------------------------------------------------------
  2618. VCB * MountedVolOfDrive(DriveRecPtr drive)
  2619. {
  2620.     VolumeRecPtr    vol = nil;
  2621.     VCB                *vcb = nil;
  2622.     
  2623.     if (drive)
  2624.     {
  2625.         vol = drive->nextVol;                // first volume (logical drive) of drive
  2626.         while (vol)
  2627.         {
  2628.             vcb = FindMountedVol(vol->vRefNum);        // check if a volume is mounted
  2629.             if (vcb)                                // if mounted volume, stop now
  2630.             {
  2631.                 break;
  2632.             }
  2633.             else                                                // next volume of drive
  2634.             {
  2635.                 vol = (VolumeRecPtr)vol->nextVol;
  2636.         }
  2637.     }
  2638.     }
  2639.     
  2640.     return(vcb);
  2641. }
  2642.  
  2643.  
  2644. //------------------------------------------------------------------------------
  2645. //    Function:        UpdateQ
  2646. //    Description:    Updates the specified drive in the system drive queue with
  2647. //                    the specified capacity
  2648. //                    
  2649. //    Input:            qDrive:        the drive to update
  2650. //                    newSize:    the new drive capacity
  2651. //-------------------------------------------------------------------------------
  2652. void UpdateQ(SInt16 qDrive, SInt32 newSize)
  2653. {
  2654.     QHdrPtr qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2655.     DrvQEl *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2656.  
  2657.     while (qel) 
  2658.     {
  2659.         // Search until drive is found, then update its capacity
  2660.         if (qel->dQDrive == qDrive)                // if drive number found, 
  2661.         {        
  2662.             qel->dQDrvSz2 = *(UInt16*)&newSize;    // new capacity (hi word)
  2663.             qel->dQDrvSz = newSize;                        // low word of capacity 
  2664.             break;
  2665.         }
  2666.         else                            // else next queue element 
  2667.             qel = (DrvQEl*) (qel->qLink);
  2668.     }
  2669. }
  2670.  
  2671. //------------------------------------------------------------------------------
  2672. //    FUNCTION:    NextPartitionID
  2673. //    PURPOSE:    Returns the next unique partition ID for all volumes associated
  2674. //                with the specified drive.  NOTE: This function should be used only
  2675. //                when adding volumes which do not have a partition map on the media.
  2676. //                
  2677. //    INPUT:        drive:    pointer to the drive record to search for next partition ID
  2678. //
  2679. //    OUTPUT:        a unique partition ID related to the specified volume
  2680. //
  2681. //-------------------------------------------------------------------------------
  2682. SInt32 NextPartitionID(DriveRecPtr drive)
  2683. {
  2684.     VolumeRecPtr vol = 0;
  2685.     SInt32 nextPartitionID = 1;                // Start search with partition ID of 1
  2686.  
  2687.     if (drive)                                // if drive is present…
  2688.     {
  2689.         vol = (VolumeRecPtr)drive->nextVol;            // first volume pointer
  2690.  
  2691.         while (vol)                            // while not end of volume queue…
  2692.         {
  2693.             if (vol->partitionNo == nextPartitionID)    // if partition ID used
  2694.             {
  2695.                 nextPartitionID++;                    // bump partition ID
  2696.                 vol = drive->nextVol;                // reset volume pointer
  2697.             }
  2698.             else
  2699.                 vol = (VolumeRecPtr)vol->nextVol;            // otherwise, point to next volume
  2700.         }
  2701.     }
  2702.     
  2703.     return(nextPartitionID);            // nil if volume not found
  2704. }
  2705.  
  2706.  
  2707. //------------------------------------------------------------------------------
  2708. //    Function:        MountVolumes
  2709. //    Description:    Mounts all volume for the specified drive.
  2710. //
  2711. //    Input:            DriveRecPtr drive:    drive with volumes to mount
  2712. //
  2713. //    Output:            Returns any errors that occur from PostEvent
  2714. //-------------------------------------------------------------------------------
  2715. OSErr MountVolumes( DriveRecPtr drive )
  2716. {
  2717.     VolumeRecPtr        vol;
  2718.     OSErr                mountErr = noErr;
  2719.                 
  2720.     // Post a Disk Inserted event for all HFS volumes not yet mounted
  2721.     if (drive)                                                        // for each drive…
  2722.     {
  2723.         vol = drive->nextVol;                                        // first volume structure
  2724.         while (vol)                                                 // for all volumes on drive…
  2725.         {                            
  2726.             if ((vol->driveStatus.diskInPlace != 0) &&                // if media in place,
  2727.                (vol->mountthispart) &&                                 // and volume to be mounted,
  2728.                (!vol->partmounted))                                    // and hasn't been done yet
  2729.             {
  2730.                 mountErr = PostEvent(diskEvt, vol->vRefNum);
  2731.  
  2732.                 if ( mountErr == noErr )
  2733.                 {
  2734.                     vol->partmounted = true;
  2735.                 }
  2736.             }
  2737.  
  2738.             vol = (VolumeRecPtr)vol->nextVol;                        // next per volume pointer
  2739.         }
  2740.     }
  2741.  
  2742.     return mountErr;                                                // return error if one occurred
  2743. }
  2744.  
  2745.  
  2746. // This is to workaround a bug in the PowerPC native version of the AddDrive
  2747. // call in systems before 8.5, where one needs to be added to the desired
  2748. // drive number before calling AddDrive.
  2749. // This function will check the system and pass the appropriate value to AddDrive
  2750. void NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl)
  2751. {
  2752.     UInt32        gestaltResponse;
  2753.  
  2754.     // Check System version to see if we need to add one to AddDrive calls
  2755.     Gestalt    (gestaltSystemVersion,(long *) &gestaltResponse);
  2756.     if( (gestaltResponse&0xFFFF) < 0x0850 )
  2757.     {
  2758.         // We are on a system before 8.5, we need to add 1 to AddDrive calls
  2759.         driveNumber += 1;
  2760.     }
  2761.  
  2762.     AddDrive( drvrRefNum, driveNumber, drvQEl);
  2763. }
  2764.  
  2765.